From f20e5668ff398139cc32e4a4cb6ade0647055ac8 Mon Sep 17 00:00:00 2001 From: bagas Date: Fri, 20 Mar 2026 14:28:43 +0700 Subject: [PATCH] feat: upgrade bubbletea, bubbles, and lipgloss to v2 (#118) Co-authored-by: Renovate-Clanker Reviewed-on: https://git.fossy.my.id/bagas/tunnel-please/pulls/118 --- .gitea/workflows/build.yml | 8 +- Dockerfile | 2 +- go.mod | 43 ++++---- go.sum | 130 +++++++++++++----------- internal/transport/httphandler.go | 2 +- internal/transport/httphandler_test.go | 2 +- renovate.json | 5 +- session/interaction/coming_soon.go | 10 +- session/interaction/commands.go | 16 +-- session/interaction/dashboard.go | 14 +-- session/interaction/interaction.go | 84 +++++++++------ session/interaction/interaction_test.go | 128 +++++++++++------------ session/interaction/model.go | 12 +-- session/interaction/slug.go | 16 +-- session/session.go | 23 ++++- 15 files changed, 271 insertions(+), 224 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 99f0d40..af3beaa 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -40,10 +40,10 @@ jobs: uses: actions/checkout@v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to Docker Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: git.fossy.my.id username: ${{ secrets.DOCKER_USERNAME }} @@ -79,7 +79,7 @@ jobs: fi - name: Build and push Docker image (release) - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . push: true @@ -97,7 +97,7 @@ jobs: if: steps.version.outputs.IS_PRERELEASE == 'false' - name: Build and push Docker image (pre-release) - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . push: true diff --git a/Dockerfile b/Dockerfile index 4d8146a..72ee416 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.26.0-alpine AS go_builder +FROM golang:1.26.1-alpine AS go_builder ARG VERSION=dev ARG BUILD_DATE=unknown diff --git a/go.mod b/go.mod index 41f5045..498e708 100644 --- a/go.mod +++ b/go.mod @@ -3,42 +3,37 @@ module tunnel_pls go 1.26.0 require ( + charm.land/bubbles/v2 v2.0.0 + charm.land/bubbletea/v2 v2.0.2 + charm.land/lipgloss/v2 v2.0.1 git.fossy.my.id/bagas/tunnel-please-grpc v1.5.0 - github.com/caddyserver/certmagic v0.25.1 - github.com/charmbracelet/bubbles v1.0.0 - github.com/charmbracelet/bubbletea v1.3.10 - github.com/charmbracelet/lipgloss v1.1.0 + github.com/caddyserver/certmagic v0.25.2 + github.com/charmbracelet/colorprofile v0.4.3 github.com/joho/godotenv v1.5.1 github.com/libdns/cloudflare v0.2.2 - github.com/muesli/termenv v0.16.0 github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.48.0 - google.golang.org/grpc v1.78.0 + google.golang.org/grpc v1.79.2 google.golang.org/protobuf v1.36.11 ) require ( github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/caddyserver/zerossl v0.1.4 // indirect - github.com/charmbracelet/colorprofile v0.4.1 // indirect + github.com/caddyserver/zerossl v0.1.5 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect github.com/charmbracelet/x/ansi v0.11.6 // indirect - github.com/charmbracelet/x/cellbuf v0.0.15 // indirect github.com/charmbracelet/x/term v0.2.2 // indirect - github.com/clipperhouse/displaywidth v0.9.0 // indirect - github.com/clipperhouse/stringish v0.1.1 // indirect - github.com/clipperhouse/uax29/v2 v2.5.0 // indirect + github.com/charmbracelet/x/termios v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.2.2 // indirect + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/libdns/libdns v1.1.1 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.19 // indirect - github.com/mholt/acmez/v3 v3.1.4 // indirect - github.com/miekg/dns v1.1.69 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/mattn/go-runewidth v0.0.20 // indirect + github.com/mholt/acmez/v3 v3.1.6 // indirect + github.com/miekg/dns v1.1.72 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -49,12 +44,12 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect go.uber.org/zap/exp v0.3.0 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.49.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.50.0 // indirect golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.41.0 // indirect + golang.org/x/sys v0.42.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/tools v0.41.0 // indirect + golang.org/x/tools v0.42.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0797acc..3931660 100644 --- a/go.sum +++ b/go.sum @@ -1,41 +1,51 @@ +charm.land/bubbles/v2 v2.0.0 h1:tE3eK/pHjmtrDiRdoC9uGNLgpopOd8fjhEe31B/ai5s= +charm.land/bubbles/v2 v2.0.0/go.mod h1:rCHoleP2XhU8um45NTuOWBPNVHxnkXKTiZqcclL/qOI= +charm.land/bubbletea/v2 v2.0.1 h1:B8e9zzK7x9JJ+XvHGF4xnYu9Xa0E0y0MyggY6dbaCfQ= +charm.land/bubbletea/v2 v2.0.1/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ= +charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0= +charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ= +charm.land/lipgloss/v2 v2.0.0 h1:sd8N/B3x892oiOjFfBQdXBQp3cAkvjGaU5TvVZC3ivo= +charm.land/lipgloss/v2 v2.0.0/go.mod h1:w6SnmsBFBmEFBodiEDurGS/sdUY/u1+v72DqUzc6J14= +charm.land/lipgloss/v2 v2.0.1 h1:6Xzrn49+Py1Um5q/wZG1gWgER2+7dUyZ9XMEufqPSys= +charm.land/lipgloss/v2 v2.0.1/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM= +code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE= +code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM= git.fossy.my.id/bagas/tunnel-please-grpc v1.5.0 h1:3xszIhck4wo9CoeRq9vnkar4PhY7kz9QrR30qj2XszA= git.fossy.my.id/bagas/tunnel-please-grpc v1.5.0/go.mod h1:Weh6ZujgWmT8XxD3Qba7sJ6r5eyUMB9XSWynqdyOoLo= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -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= -github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= -github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= -github.com/caddyserver/certmagic v0.25.1 h1:4sIKKbOt5pg6+sL7tEwymE1x2bj6CHr80da1CRRIPbY= -github.com/caddyserver/certmagic v0.25.1/go.mod h1:VhyvndxtVton/Fo/wKhRoC46Rbw1fmjvQ3GjHYSQTEY= -github.com/caddyserver/zerossl v0.1.4 h1:CVJOE3MZeFisCERZjkxIcsqIH4fnFdlYWnPYeFtBHRw= -github.com/caddyserver/zerossl v0.1.4/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= -github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= -github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= -github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= -github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= -github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/aymanbagabas/go-udiff v0.4.0 h1:TKnLPh7IbnizJIBKFWa9mKayRUBQ9Kh1BPCk6w2PnYM= +github.com/aymanbagabas/go-udiff v0.4.0/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w= +github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc= +github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg= +github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE= +github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY= +github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8= +github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= +github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= +github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA= +github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98= github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= -github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= -github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= -github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= -github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA= +github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= -github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA= -github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA= -github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= -github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U= -github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= +github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= +github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM= +github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -52,28 +62,24 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU= +github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk= +github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U= +github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ= github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI= github.com/libdns/cloudflare v0.2.2/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60= github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U= github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -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.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ= -github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= -github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= +github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk= +github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= 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/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= @@ -94,16 +100,16 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -116,28 +122,28 @@ golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/transport/httphandler.go b/internal/transport/httphandler.go index 67aa6fb..63b4b53 100644 --- a/internal/transport/httphandler.go +++ b/internal/transport/httphandler.go @@ -97,7 +97,7 @@ func (hh *httpHandler) Handler(conn net.Conn, isTLS bool) { Type: types.TunnelTypeHTTP, }) if err != nil { - _ = hh.redirect(conn, http.StatusMovedPermanently, fmt.Sprintf("https://tunnl.live/tunnel-not-found?slug=%s\r\n", slug)) + _ = hh.redirect(conn, http.StatusMovedPermanently, fmt.Sprintf("https://tunel.live/tunnel-not-found?slug=%s\r\n", slug)) return } diff --git a/internal/transport/httphandler_test.go b/internal/transport/httphandler_test.go index 6801b22..58b64b3 100644 --- a/internal/transport/httphandler_test.go +++ b/internal/transport/httphandler_test.go @@ -290,7 +290,7 @@ func TestHandler(t *testing.T) { isTLS: true, redirectTLS: false, request: []byte("GET / HTTP/1.1\r\nHost: test.domain\r\n\r\n"), - expected: []byte("HTTP/1.1 301 Moved Permanently\r\nLocation: https://tunnl.live/tunnel-not-found?slug=test\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"), + expected: []byte("HTTP/1.1 301 Moved Permanently\r\nLocation: https://tunel.live/tunnel-not-found?slug=test\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"), setupMocks: func(msr *MockSessionRegistry) { msr.On("Get", types.SessionKey{ Id: "test", diff --git a/renovate.json b/renovate.json index 4aceacc..84543d9 100644 --- a/renovate.json +++ b/renovate.json @@ -11,8 +11,9 @@ "digest" ], "automerge": true, - "baseBranchPatterns": [ - "staging" + "groupName": "all-dependencies", + "matchPackageNames": [ + "*" ] } ] diff --git a/session/interaction/coming_soon.go b/session/interaction/coming_soon.go index 006dd33..064b8ac 100644 --- a/session/interaction/coming_soon.go +++ b/session/interaction/coming_soon.go @@ -3,14 +3,14 @@ package interaction import ( "strings" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" ) -func (m *model) comingSoonUpdate(msg tea.KeyMsg) (tea.Model, tea.Cmd) { +func (m *model) comingSoonUpdate(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { m.showingComingSoon = false - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink } func (m *model) comingSoonView() string { diff --git a/session/interaction/commands.go b/session/interaction/commands.go index 5e24368..e82a405 100644 --- a/session/interaction/commands.go +++ b/session/interaction/commands.go @@ -4,10 +4,10 @@ import ( "strings" "time" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" ) func (m *model) handleCommandSelection(item commandItem) (tea.Model, tea.Cmd) { @@ -17,24 +17,24 @@ func (m *model) handleCommandSelection(item commandItem) (tea.Model, tea.Cmd) { m.editingSlug = true m.slugInput.SetValue(m.interaction.slug.String()) m.slugInput.Focus() - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink case "tunnel-type": m.showingCommands = false m.showingComingSoon = true - return m, tea.Batch(tickCmd(5*time.Second), tea.ClearScreen, textinput.Blink) + return m, tickCmd(5 * time.Second) default: m.showingCommands = false return m, nil } } -func (m *model) commandsUpdate(msg tea.KeyMsg) (tea.Model, tea.Cmd) { +func (m *model) commandsUpdate(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch { case key.Matches(msg, m.keymap.quit), msg.String() == "esc": m.showingCommands = false - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink case msg.String() == "enter": selectedItem := m.commandList.SelectedItem() if selectedItem != nil { diff --git a/session/interaction/dashboard.go b/session/interaction/dashboard.go index cf10ddb..853c327 100644 --- a/session/interaction/dashboard.go +++ b/session/interaction/dashboard.go @@ -4,20 +4,20 @@ import ( "fmt" "strings" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" ) -func (m *model) dashboardUpdate(msg tea.KeyMsg) (tea.Model, tea.Cmd) { +func (m *model) dashboardUpdate(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { switch { case key.Matches(msg, m.keymap.quit): m.quitting = true - return m, tea.Batch(tea.ClearScreen, textinput.Blink, tea.Quit) + return m, tea.Batch(textinput.Blink, tea.Quit) case key.Matches(msg, m.keymap.command): m.showingCommands = true - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink } return m, nil } diff --git a/session/interaction/interaction.go b/session/interaction/interaction.go index 814cd67..ffbf303 100644 --- a/session/interaction/interaction.go +++ b/session/interaction/interaction.go @@ -9,13 +9,12 @@ import ( "tunnel_pls/session/slug" "tunnel_pls/types" - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/list" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/muesli/termenv" + "charm.land/bubbles/v2/help" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/list" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "github.com/charmbracelet/colorprofile" "golang.org/x/crypto/ssh" ) @@ -54,6 +53,8 @@ type interaction struct { cancel context.CancelFunc mode types.InteractiveMode programMu sync.Mutex + width int + height int } func (i *interaction) SetMode(m types.InteractiveMode) { @@ -72,8 +73,14 @@ func (i *interaction) Send(message string) error { return nil } func (i *interaction) SetWH(w, h int) { - if i.program != nil { - i.program.Send(tea.WindowSizeMsg{ + i.programMu.Lock() + i.width = w + i.height = h + prog := i.program + i.programMu.Unlock() + + if prog != nil { + prog.Send(tea.WindowSizeMsg{ Width: w, Height: h, }) @@ -120,7 +127,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tickMsg: m.showingComingSoon = false - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink case tea.WindowSizeMsg: m.width = msg.Width @@ -129,17 +136,17 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.commandList.SetHeight(msg.Height - 4) if msg.Width < 80 { - m.slugInput.Width = msg.Width - 10 + m.slugInput.SetWidth(msg.Width - 10) } else { - m.slugInput.Width = 50 + m.slugInput.SetWidth(50) } return m, nil case tea.QuitMsg: m.quitting = true - return m, tea.Batch(tea.ClearScreen, textinput.Blink, tea.Quit) + return m, tea.Batch(textinput.Blink, tea.Quit) - case tea.KeyMsg: + case tea.KeyPressMsg: if m.showingComingSoon { return m.comingSoonUpdate(msg) } @@ -159,36 +166,43 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (i *interaction) Redraw() { - if i.program != nil { - i.program.Send(tea.ClearScreen()) + i.programMu.Lock() + prog := i.program + w, h := i.width, i.height + i.programMu.Unlock() + + if prog != nil { + prog.Send(tea.WindowSizeMsg{Width: w, Height: h}) } } -func (m *model) View() string { +func (m *model) View() tea.View { if m.quitting { - return "" + return tea.NewView("") } if m.showingComingSoon { - return m.comingSoonView() + return tea.NewView(m.comingSoonView()) } if m.editingSlug { - return m.slugView() + return tea.NewView(m.slugView()) } if m.showingCommands { - return m.commandsView() + return tea.NewView(m.commandsView()) } - return m.dashboardView() + v := tea.NewView(m.dashboardView()) + v.AltScreen = true + v.MouseMode = tea.MouseModeCellMotion + return v } func (i *interaction) Start() { if i.mode == types.InteractiveModeHEADLESS { return } - lipgloss.SetColorProfile(termenv.TrueColor) protocol := "http" if i.config.TLSEnabled() { @@ -216,7 +230,7 @@ func (i *interaction) Start() { ti := textinput.New() ti.Placeholder = "my-custom-slug" ti.CharLimit = 20 - ti.Width = 50 + ti.SetWidth(50) m := &model{ randomizer: i.randomizer, @@ -245,19 +259,29 @@ func (i *interaction) Start() { } i.programMu.Lock() - i.program = tea.NewProgram( - m, + w, h := i.width, i.height + i.programMu.Unlock() + + opts := []tea.ProgramOption{ tea.WithInput(i.channel), tea.WithOutput(i.channel), - tea.WithAltScreen(), - tea.WithMouseCellMotion(), + tea.WithColorProfile(colorprofile.TrueColor), tea.WithoutSignals(), tea.WithoutSignalHandler(), tea.WithFPS(30), - ) + tea.WithEnvironment([]string{"TERM=xterm-256color", "COLORTERM=truecolor"}), + } + if w > 0 && h > 0 { + opts = append(opts, tea.WithWindowSize(w, h)) + } + + prog := tea.NewProgram(m, opts...) + + i.programMu.Lock() + i.program = prog i.programMu.Unlock() - _, err := i.program.Run() + _, err := prog.Run() if err != nil { log.Printf("Cannot close tea: %s \n", err) } diff --git a/session/interaction/interaction_test.go b/session/interaction/interaction_test.go index 4992093..03115d3 100644 --- a/session/interaction/interaction_test.go +++ b/session/interaction/interaction_test.go @@ -9,10 +9,10 @@ import ( "time" "tunnel_pls/types" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/list" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/list" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "golang.org/x/crypto/ssh" @@ -459,7 +459,7 @@ func TestInteraction_Start(t *testing.T) { mockInteraction := New(mockRandom, mockConfig, mockSlug, mockForwarder, mockSessionRegistry, "user", mockCloser.Close) mockInteraction.SetMode(tt.mode) - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(tt.tlsEnabled) mockForwarder.On("TunnelType").Return(tt.tunnelType) mockForwarder.On("ForwardedPort").Return(tt.port) @@ -525,7 +525,7 @@ func TestModel_Update(t *testing.T) { m := &model{ randomizer: mockRandom, - domain: "tunnl.live", + domain: "tunel.live", protocol: "http", tunnelType: types.TunnelTypeHTTP, port: 8080, @@ -622,7 +622,7 @@ func TestModel_View(t *testing.T) { m := &model{ randomizer: mockRandom, - domain: "tunnl.live", + domain: "tunel.live", protocol: "http", tunnelType: types.TunnelTypeHTTP, port: 8080, @@ -651,9 +651,9 @@ func TestModel_View(t *testing.T) { view := m.View() if tt.expectedEmpty { - assert.Empty(t, view) + assert.Empty(t, view.Content) } else { - assert.NotEmpty(t, view) + assert.NotEmpty(t, view.Content) } }) } @@ -694,7 +694,7 @@ func TestInteraction_Integration(t *testing.T) { func TestModel_Update_KeyMessages(t *testing.T) { tests := []struct { name string - key tea.KeyMsg + key tea.KeyPressMsg showingComingSoon bool editingSlug bool showingCommands bool @@ -702,25 +702,25 @@ func TestModel_Update_KeyMessages(t *testing.T) { }{ { name: "key press while showing coming soon", - key: tea.KeyMsg{Type: tea.KeyEnter}, + key: tea.KeyPressMsg{Code: tea.KeyEnter}, showingComingSoon: true, description: "should call comingSoonUpdate", }, { name: "key press while editing slug", - key: tea.KeyMsg{Type: tea.KeyEnter}, + key: tea.KeyPressMsg{Code: tea.KeyEnter}, editingSlug: true, description: "should call slugUpdate", }, { name: "key press while showing commands", - key: tea.KeyMsg{Type: tea.KeyEnter}, + key: tea.KeyPressMsg{Code: tea.KeyEnter}, showingCommands: true, description: "should call commandsUpdate", }, { name: "key press in dashboard view", - key: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'c'}}, + key: tea.KeyPressMsg{Code: 'c', Text: "c"}, description: "should call dashboardUpdate", }, } @@ -742,7 +742,7 @@ func TestModel_Update_KeyMessages(t *testing.T) { m := &model{ randomizer: mockRandom, - domain: "tunnl.live", + domain: "tunel.live", protocol: "http", tunnelType: types.TunnelTypeHTTP, port: 8080, @@ -777,7 +777,7 @@ func TestModel_SlugUpdate(t *testing.T) { tests := []struct { name string tunnelType types.TunnelType - keyMsg tea.KeyMsg + keyMsg tea.KeyPressMsg inputValue string setupMocks func(*MockSessionRegistry, *MockSlug, *MockRandom) expectedEdit bool @@ -787,21 +787,21 @@ func TestModel_SlugUpdate(t *testing.T) { { name: "escape key cancels editing", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyEsc}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEsc}, setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) {}, expectedEdit: false, }, { name: "ctrl+c cancels editing", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyCtrlC}, + keyMsg: tea.KeyPressMsg{Code: 'c', Mod: tea.ModCtrl}, setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) {}, expectedEdit: false, }, { name: "enter key saves valid slug", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyEnter}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEnter}, inputValue: "my-custom-slug", setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) { ms.On("String").Return("old-slug") @@ -816,7 +816,7 @@ func TestModel_SlugUpdate(t *testing.T) { { name: "enter key with error shows error message", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyEnter}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEnter}, inputValue: "invalid", setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) { ms.On("String").Return("old-slug") @@ -831,7 +831,7 @@ func TestModel_SlugUpdate(t *testing.T) { { name: "ctrl+r generates random slug", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyCtrlR}, + keyMsg: tea.KeyPressMsg{Code: 'r', Mod: tea.ModCtrl}, setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) { mr.On("String", 20).Return("random-generated-slug", nil) }, @@ -841,7 +841,7 @@ func TestModel_SlugUpdate(t *testing.T) { { name: "ctrl+r with error does nothing", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyCtrlR}, + keyMsg: tea.KeyPressMsg{Code: 'r', Mod: tea.ModCtrl}, setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) { mr.On("String", 20).Return("", assert.AnError) }, @@ -850,14 +850,14 @@ func TestModel_SlugUpdate(t *testing.T) { { name: "regular key updates input", tunnelType: types.TunnelTypeHTTP, - keyMsg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'a'}}, + keyMsg: tea.KeyPressMsg{Code: 'a', Text: "a"}, setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) {}, expectedEdit: true, }, { name: "tcp tunnel exits editing immediately", tunnelType: types.TunnelTypeTCP, - keyMsg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'a'}}, + keyMsg: tea.KeyPressMsg{Code: 'a', Text: "a"}, setupMocks: func(msr *MockSessionRegistry, ms *MockSlug, mr *MockRandom) {}, expectedEdit: false, }, @@ -879,7 +879,7 @@ func TestModel_SlugUpdate(t *testing.T) { m := &model{ randomizer: mockRandom, - domain: "tunnl.live", + domain: "tunel.live", protocol: "http", tunnelType: tt.tunnelType, port: 8080, @@ -986,7 +986,7 @@ func TestModel_SlugView(t *testing.T) { m := &model{ randomizer: mockRandom, - domain: "tunnl.live", + domain: "tunel.live", protocol: "http", tunnelType: tt.tunnelType, port: 8080, @@ -1006,19 +1006,19 @@ func TestModel_SlugView(t *testing.T) { func TestModel_ComingSoonUpdate(t *testing.T) { tests := []struct { name string - keyMsg tea.KeyMsg + keyMsg tea.KeyPressMsg }{ { name: "any key dismisses coming soon", - keyMsg: tea.KeyMsg{Type: tea.KeyEnter}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEnter}, }, { name: "escape key dismisses", - keyMsg: tea.KeyMsg{Type: tea.KeyEsc}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEsc}, }, { name: "space key dismisses", - keyMsg: tea.KeyMsg{Type: tea.KeySpace}, + keyMsg: tea.KeyPressMsg{Code: tea.KeySpace}, }, } @@ -1097,7 +1097,7 @@ func TestModel_ComingSoonView(t *testing.T) { func TestModel_CommandsUpdate(t *testing.T) { tests := []struct { name string - keyMsg tea.KeyMsg + keyMsg tea.KeyPressMsg selectedItem list.Item expectCommands bool expectEditSlug bool @@ -1105,31 +1105,31 @@ func TestModel_CommandsUpdate(t *testing.T) { }{ { name: "escape key closes commands", - keyMsg: tea.KeyMsg{Type: tea.KeyEsc}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEsc}, expectCommands: false, }, { name: "q key closes commands", - keyMsg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}}, + keyMsg: tea.KeyPressMsg{Code: 'q', Text: "q"}, expectCommands: false, }, { name: "enter on slug command starts editing", - keyMsg: tea.KeyMsg{Type: tea.KeyEnter}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEnter}, selectedItem: commandItem{name: "slug", desc: "Set custom subdomain"}, expectCommands: false, expectEditSlug: true, }, { name: "enter on tunnel-type shows coming soon", - keyMsg: tea.KeyMsg{Type: tea.KeyEnter}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyEnter}, selectedItem: commandItem{name: "tunnel-type", desc: "Change tunnel type"}, expectCommands: false, expectComingSoon: true, }, { name: "arrow key navigates list", - keyMsg: tea.KeyMsg{Type: tea.KeyDown}, + keyMsg: tea.KeyPressMsg{Code: tea.KeyDown}, expectCommands: true, }, } @@ -1261,28 +1261,28 @@ func TestModel_CommandsView(t *testing.T) { func TestModel_DashboardUpdate(t *testing.T) { tests := []struct { name string - keyMsg tea.KeyMsg + keyMsg tea.KeyPressMsg expectQuit bool expectCommands bool }{ { name: "q key quits", - keyMsg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}}, + keyMsg: tea.KeyPressMsg{Code: 'q', Text: "q"}, expectQuit: true, }, { name: "ctrl+c quits", - keyMsg: tea.KeyMsg{Type: tea.KeyCtrlC}, + keyMsg: tea.KeyPressMsg{Code: 'c', Mod: tea.ModCtrl}, expectQuit: true, }, { name: "c key opens commands", - keyMsg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'c'}}, + keyMsg: tea.KeyPressMsg{Code: 'c', Text: "c"}, expectCommands: true, }, { name: "other keys do nothing", - keyMsg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}}, + keyMsg: tea.KeyPressMsg{Code: 'x', Text: "x"}, }, } @@ -1398,7 +1398,7 @@ func TestModel_DashboardView(t *testing.T) { m := &model{ randomizer: mockRandom, - domain: "tunnl.live", + domain: "tunel.live", protocol: tt.protocol, tunnelType: tt.tunnelType, port: tt.port, @@ -1550,8 +1550,8 @@ func TestBuildURL(t *testing.T) { name: "http url", protocol: "http", subdomain: "test", - domain: "tunnl.live", - expected: "http://test.tunnl.live", + domain: "tunel.live", + expected: "http://test.tunel.live", }, { name: "https url", @@ -1564,8 +1564,8 @@ func TestBuildURL(t *testing.T) { name: "custom subdomain", protocol: "http", subdomain: "my-custom-slug", - domain: "tunnl.live", - expected: "http://my-custom-slug.tunnl.live", + domain: "tunel.live", + expected: "http://my-custom-slug.tunel.live", }, } @@ -1712,30 +1712,30 @@ func TestModel_GetTunnelURL(t *testing.T) { tunnelType: types.TunnelTypeHTTP, protocol: "http", slug: "my-app", - domain: "tunnl.live", - expected: "http://my-app.tunnl.live", + domain: "tunel.live", + expected: "http://my-app.tunel.live", }, { name: "https tunnel", tunnelType: types.TunnelTypeHTTP, protocol: "https", slug: "secure-app", - domain: "tunnl.live", - expected: "https://secure-app.tunnl.live", + domain: "tunel.live", + expected: "https://secure-app.tunel.live", }, { name: "tcp tunnel", tunnelType: types.TunnelTypeTCP, - domain: "tunnl.live", + domain: "tunel.live", port: 8080, - expected: "tcp://tunnl.live:8080", + expected: "tcp://tunel.live:8080", }, { name: "tcp tunnel with different port", tunnelType: types.TunnelTypeTCP, - domain: "tunnl.live", + domain: "tunel.live", port: 3306, - expected: "tcp://tunnl.live:3306", + expected: "tcp://tunel.live:3306", }, } @@ -1797,28 +1797,28 @@ func TestInteraction_Start_Interactive(t *testing.T) { tlsEnabled: false, tunnelType: types.TunnelTypeHTTP, port: 8080, - domain: "tunnl.live", + domain: "tunel.live", }, { name: "interactive mode with https", tlsEnabled: true, tunnelType: types.TunnelTypeHTTP, port: 8443, - domain: "secure.tunnl.live", + domain: "secure.tunel.live", }, { name: "interactive mode with tcp", tlsEnabled: false, tunnelType: types.TunnelTypeTCP, port: 3306, - domain: "db.tunnl.live", + domain: "db.tunel.live", }, { name: "interactive mode with tcp and tls enabled", tlsEnabled: true, tunnelType: types.TunnelTypeTCP, port: 5432, - domain: "postgres.tunnl.live", + domain: "postgres.tunel.live", }, } @@ -1901,7 +1901,7 @@ func TestInteraction_Start_ProtocolSelection(t *testing.T) { mockSessionRegistry := &MockSessionRegistry{} closeFunc := func() error { return nil } - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(tt.tlsEnabled) mockForwarder.On("TunnelType").Return(types.TunnelTypeHTTP) mockForwarder.On("ForwardedPort").Return(uint16(8080)) @@ -1966,7 +1966,7 @@ func TestInteraction_Stop(t *testing.T) { i := mockInteraction.(*interaction) if tt.setupProgram { - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(false) mockForwarder.On("TunnelType").Return(types.TunnelTypeHTTP) mockForwarder.On("ForwardedPort").Return(uint16(8080)) @@ -2007,7 +2007,7 @@ func TestInteraction_Start_CommandListSetup(t *testing.T) { mockSessionRegistry := &MockSessionRegistry{} closeFunc := func() error { return nil } - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(false) mockForwarder.On("TunnelType").Return(types.TunnelTypeHTTP) mockForwarder.On("ForwardedPort").Return(uint16(8080)) @@ -2043,7 +2043,7 @@ func TestInteraction_Start_TextInputSetup(t *testing.T) { mockSessionRegistry := &MockSessionRegistry{} closeFunc := func() error { return nil } - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(false) mockForwarder.On("TunnelType").Return(types.TunnelTypeHTTP) mockForwarder.On("ForwardedPort").Return(uint16(8080)) @@ -2107,7 +2107,7 @@ func TestInteraction_Start_CleanupOnExit(t *testing.T) { } } - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(false) mockForwarder.On("TunnelType").Return(types.TunnelTypeHTTP) mockForwarder.On("ForwardedPort").Return(uint16(8080)) @@ -2171,7 +2171,7 @@ func TestInteraction_Start_WithDifferentChannels(t *testing.T) { mockSessionRegistry := &MockSessionRegistry{} closeFunc := func() error { return nil } - mockConfig.On("Domain").Return("tunnl.live") + mockConfig.On("Domain").Return("tunel.live") mockConfig.On("TLSEnabled").Return(false) mockForwarder.On("TunnelType").Return(types.TunnelTypeHTTP) mockForwarder.On("ForwardedPort").Return(uint16(8080)) diff --git a/session/interaction/model.go b/session/interaction/model.go index c0d1672..f972066 100644 --- a/session/interaction/model.go +++ b/session/interaction/model.go @@ -6,11 +6,11 @@ import ( "tunnel_pls/internal/random" "tunnel_pls/types" - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/list" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" + "charm.land/bubbles/v2/help" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/list" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" ) type commandItem struct { @@ -77,7 +77,7 @@ type keymap struct { type tickMsg time.Time func (m *model) Init() tea.Cmd { - return tea.Batch(textinput.Blink, tea.WindowSize()) + return tea.Batch(textinput.Blink, tea.RequestWindowSize) } func getResponsiveWidth(screenWidth, padding, minWidth, maxWidth int) int { diff --git a/session/interaction/slug.go b/session/interaction/slug.go index ff57cb1..afacecc 100644 --- a/session/interaction/slug.go +++ b/session/interaction/slug.go @@ -5,26 +5,26 @@ import ( "strings" "tunnel_pls/types" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" ) -func (m *model) slugUpdate(msg tea.KeyMsg) (tea.Model, tea.Cmd) { +func (m *model) slugUpdate(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { var cmd tea.Cmd if m.tunnelType != types.TunnelTypeHTTP { m.editingSlug = false m.slugError = "" - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink } switch msg.String() { case "esc", "ctrl+c": m.editingSlug = false m.slugError = "" - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink case "enter": inputValue := m.slugInput.Value() if err := m.interaction.sessionRegistry.Update(m.interaction.user, types.SessionKey{ @@ -39,7 +39,7 @@ func (m *model) slugUpdate(msg tea.KeyMsg) (tea.Model, tea.Cmd) { } m.editingSlug = false m.slugError = "" - return m, tea.Batch(tea.ClearScreen, textinput.Blink) + return m, textinput.Blink default: if key.Matches(msg, m.keymap.random) { newSubdomain, err := m.randomizer.String(20) diff --git a/session/session.go b/session/session.go index cc27c4c..2062d6a 100644 --- a/session/session.go +++ b/session/session.go @@ -232,13 +232,34 @@ func (s *session) handleWindowChange(req *ssh.Request) error { return req.Reply(true, nil) } +func (s *session) handlePtyReq(req *ssh.Request) error { + var ptyPayload struct { + Term string + Width uint32 + Height uint32 + PxW uint32 + PxH uint32 + Modes string + } + if err := ssh.Unmarshal(req.Payload, &ptyPayload); err == nil { + if ptyPayload.Width > 0 && ptyPayload.Height > 0 { + s.interaction.SetWH(int(ptyPayload.Width), int(ptyPayload.Height)) + } + } + return req.Reply(true, nil) +} + func (s *session) HandleGlobalRequest(GlobalRequest <-chan *ssh.Request) error { for req := range GlobalRequest { switch req.Type { - case "shell", "pty-req": + case "shell": if err := req.Reply(true, nil); err != nil { return err } + case "pty-req": + if err := s.handlePtyReq(req); err != nil { + return err + } case "window-change": if err := s.handleWindowChange(req); err != nil { return err