From 0ce024f433324e41b11b63fe37df4113382be696 Mon Sep 17 00:00:00 2001 From: whoop-t Date: Fri, 23 Jun 2023 17:06:17 -0700 Subject: [PATCH 1/6] feat: add zone for mouse, working without heading --- go.mod | 27 ++++++++++++----------- go.sum | 67 ++++++++++++++++++++++++++------------------------------- main.go | 62 +++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 96 insertions(+), 60 deletions(-) diff --git a/go.mod b/go.mod index 2ffbc5d..3d778eb 100644 --- a/go.mod +++ b/go.mod @@ -3,32 +3,33 @@ module github.com/savannahostrowski/gruyere go 1.20 require ( - github.com/charmbracelet/bubbletea v0.23.2 + github.com/charmbracelet/bubbletea v0.24.2 github.com/charmbracelet/log v0.1.1 + github.com/lrstanley/bubblezone v0.0.0-20230507010339-3326b9492591 ) require ( github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect ) require ( - github.com/aymanbagabas/go-osc52 v1.2.1 // indirect - github.com/charmbracelet/bubbles v0.15.0 - github.com/charmbracelet/lipgloss v0.6.0 - github.com/containerd/console v1.0.3 // indirect + github.com/charmbracelet/bubbles v0.16.1 + github.com/charmbracelet/lipgloss v0.7.1 + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // 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.14.0 // indirect - github.com/rivo/uniseg v0.4.2 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 - golang.org/x/text v0.7.0 // indirect + github.com/muesli/termenv v0.15.1 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/term v0.9.0 + golang.org/x/text v0.10.0 // indirect ) diff --git a/go.sum b/go.sum index a0189ea..ffebb61 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,32 @@ 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 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E= -github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI= -github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74= -github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -github.com/charmbracelet/bubbletea v0.23.2 h1:vuUJ9HJ7b/COy4I30e8xDVQ+VRDUEFykIjryPfgsdps= -github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= +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/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= +github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= +github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= +github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= +github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= +github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/charmbracelet/log v0.1.1 h1:XBqTzrgT+7BenzFkb4DP3rt6VKLtx/zpgNMfJMAdbCc= github.com/charmbracelet/log v0.1.1/go.mod h1:nbG+jJlMwbPM8v2wFyPG6qCr9flLQ1XTS+fhzUWd6JE= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 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/lrstanley/bubblezone v0.0.0-20230507010339-3326b9492591 h1:Avu2JFiePQf2ntjQgLUk854ZBLiQvXayW/BjvFCtIzI= +github.com/lrstanley/bubblezone v0.0.0-20230507010339-3326b9492591/go.mod h1:v5lEwWaguF1o2MW/ucO0ZIA/IZymdBYJJ+2cMRLE7LU= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/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.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= @@ -35,24 +34,22 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= -github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= -github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +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/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.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= 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.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/muesli/termenv v0.14.0 h1:8x9NFfOe8lmIWK4pgy3IfVEy47f+ppe3tUqdPZG2Uy0= -github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8= +github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= +github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= 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.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.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= -github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -62,22 +59,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go index 1320659..48bc90d 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/log" + zone "github.com/lrstanley/bubblezone" "github.com/lucasb-eyer/go-colorful" "golang.org/x/term" ) @@ -68,9 +69,9 @@ type item struct { desc string } -func (i item) Title() string { return i.title } +func (i item) Title() string { return zone.Mark(i.title, i.title) } func (i item) Description() string { return i.desc } -func (i item) FilterValue() string { return i.title } +func (i item) FilterValue() string { return zone.Mark(i.title, i.title) } type model struct { list list.Model @@ -83,7 +84,7 @@ var doc = strings.Builder{} type tickMsg time.Time func (m model) Init() tea.Cmd { - renderTitle() + // renderTitle() return tickCmd() } @@ -93,7 +94,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if msg.String() == "ctrl+c" { return m, tea.Quit } - + // If there are no running processes, dont allow user to select hasRunningProcesses := len(m.list.Items()) > 0 if msg.String() == "enter" && hasRunningProcesses { @@ -123,6 +124,45 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.activeButton = "yes" } + // Mouse handlers + case tea.MouseMsg: + if msg.Type == tea.MouseWheelUp { + m.list.CursorUp() + return m, nil + } + + if msg.Type == tea.MouseWheelDown { + m.list.CursorDown() + return m, nil + } + + if msg.Type == tea.MouseRelease { + if m.selectedPort == "" { + for i, listItem := range m.list.VisibleItems() { + item, _ := listItem.(item) + // Check each item to see if it's in bounds. + if zone.Get(item.title).InBounds(msg) { + // If so, select it in the list. + port := m.list.Items()[i].FilterValue() + m.selectedPort = port + break + } + } + // If ok is clicked + } else if zone.Get("ok").InBounds(msg) { + rgx := regexp.MustCompile(`\((.*?)\)`) + pid := rgx.FindStringSubmatch(m.list.SelectedItem().FilterValue())[1] + killPort(pid) + // Get running processes again when a process is killed + m.list.SetItems(getProcesses()) + m.list.ResetFilter() + m.selectedPort = "" + // If no is clicked + } else if zone.Get("no").InBounds(msg) { + m.selectedPort = "" + } + } + case tickMsg: cmd := m.list.SetItems(getProcesses()) return m, tea.Batch(tickCmd(), cmd) @@ -140,15 +180,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View() string { // If there's a selected port, render the confirmation dialog if m.selectedPort != "" { - return confirmationView(m) + return zone.Scan(confirmationView(m)) } - m.list.SetHeight(20) // Otherwise, we just show the list of processes - return docStyle.Render(m.list.View()) + return zone.Scan(docStyle.Render(m.list.View())) } func main() { + // init mouse support via zone + zone.NewGlobal() + // Get processes running on listening ports processes := getProcesses() @@ -164,7 +206,7 @@ func main() { m.list.SetShowTitle(false) // Let 'er rip - p := tea.NewProgram(m) + p := tea.NewProgram(m, tea.WithMouseCellMotion()) if _, err := p.Run(); err != nil { log.Fatal("Error running program:", err) @@ -185,7 +227,7 @@ func getProcesses() []list.Item { if err != nil { return []list.Item{} } - + strStdout := string(out) procs := strings.Split(strStdout, "\n") @@ -236,7 +278,7 @@ func confirmationView(m model) string { qStr := fmt.Sprintf("Are you sure you want to kill port %s?", m.selectedPort) question := lipgloss.NewStyle().Width(50).Align(lipgloss.Center).Render(qStr) - buttons := lipgloss.JoinHorizontal(lipgloss.Top, okButton, cancelButton) + buttons := lipgloss.JoinHorizontal(lipgloss.Top, zone.Mark("ok", okButton), zone.Mark("no", cancelButton)) ui := lipgloss.JoinVertical(lipgloss.Center, question, buttons) dialog := lipgloss.Place(width, 9, From 884a2259473d3691a5e938277c565e2adc4650b3 Mon Sep 17 00:00:00 2001 From: whoop-t Date: Fri, 23 Jun 2023 17:50:53 -0700 Subject: [PATCH 2/6] fix: refactor title rendering, fullscreen mode(zone only supports fullscreen) --- main.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 48bc90d..e55c101 100644 --- a/main.go +++ b/main.go @@ -77,6 +77,7 @@ type model struct { list list.Model selectedPort string activeButton string + title string } var doc = strings.Builder{} @@ -84,7 +85,6 @@ var doc = strings.Builder{} type tickMsg time.Time func (m model) Init() tea.Cmd { - // renderTitle() return tickCmd() } @@ -148,7 +148,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { break } } - // If ok is clicked + // If ok is clicked } else if zone.Get("ok").InBounds(msg) { rgx := regexp.MustCompile(`\((.*?)\)`) pid := rgx.FindStringSubmatch(m.list.SelectedItem().FilterValue())[1] @@ -157,7 +157,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.list.SetItems(getProcesses()) m.list.ResetFilter() m.selectedPort = "" - // If no is clicked + // If no is clicked } else if zone.Get("no").InBounds(msg) { m.selectedPort = "" } @@ -178,13 +178,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m model) View() string { + var view string // If there's a selected port, render the confirmation dialog if m.selectedPort != "" { - return zone.Scan(confirmationView(m)) + view = confirmationView(m) + } else { + // Otherwise, we just show the list of processes + m.list.SetHeight(20) + view = docStyle.Render(m.list.View()) } - // Otherwise, we just show the list of processes - return zone.Scan(docStyle.Render(m.list.View())) + return zone.Scan(lipgloss.JoinVertical(lipgloss.Top, m.title, view)) } func main() { @@ -204,9 +208,12 @@ func main() { m.list.SetStatusBarItemName("process", "processes") //Hide default list title + styles m.list.SetShowTitle(false) + m.title = initTitle() // Let 'er rip - p := tea.NewProgram(m, tea.WithMouseCellMotion()) + // Note: WithAltScreen is needed or zone(mouse support) will break + // See https://github.com/lrstanley/bubblezone/issues/11 + p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion()) if _, err := p.Run(); err != nil { log.Fatal("Error running program:", err) @@ -291,7 +298,7 @@ func confirmationView(m model) string { return baseStyle.Render(dialog + "\n\n") } -func renderTitle() { +func initTitle() string { colors := colorGrid(1, 5) var title strings.Builder @@ -312,7 +319,7 @@ func renderTitle() { row := lipgloss.JoinHorizontal(lipgloss.Top, title.String(), desc) doc.WriteString(row + "\n\n") - fmt.Println(docStyle.Render(doc.String())) + return docStyle.Render(doc.String()) } // Via https://github.com/charmbracelet/lipgloss/blob/776c15f0da16d2b1058a079ec6a08a2e1170d721/examples/layout/main.go#L338 From c7fc039880c010a213b16a59d9451486ab9d1557 Mon Sep 17 00:00:00 2001 From: whoop-t Date: Fri, 23 Jun 2023 18:13:43 -0700 Subject: [PATCH 3/6] refactor: method to exec killing port --- main.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index e55c101..be4ae17 100644 --- a/main.go +++ b/main.go @@ -104,12 +104,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } else { // If accepted killing the port, grab PID + execute killPort() if m.activeButton == "yes" { - rgx := regexp.MustCompile(`\((.*?)\)`) - pid := rgx.FindStringSubmatch(m.list.SelectedItem().FilterValue())[1] - killPort(pid) - // Get running processes again when a process is killed - m.list.SetItems(getProcesses()) - m.list.ResetFilter() + execPortKill(m) } // In all cases, reset selected port at the end m.selectedPort = "" @@ -143,6 +138,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Check each item to see if it's in bounds. if zone.Get(item.title).InBounds(msg) { // If so, select it in the list. + m.list.Select(i) port := m.list.Items()[i].FilterValue() m.selectedPort = port break @@ -150,12 +146,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } // If ok is clicked } else if zone.Get("ok").InBounds(msg) { - rgx := regexp.MustCompile(`\((.*?)\)`) - pid := rgx.FindStringSubmatch(m.list.SelectedItem().FilterValue())[1] - killPort(pid) - // Get running processes again when a process is killed - m.list.SetItems(getProcesses()) - m.list.ResetFilter() + execPortKill(m) m.selectedPort = "" // If no is clicked } else if zone.Get("no").InBounds(msg) { @@ -183,7 +174,7 @@ func (m model) View() string { if m.selectedPort != "" { view = confirmationView(m) } else { - // Otherwise, we just show the list of processes + // Otherwise, we just show the list of processes m.list.SetHeight(20) view = docStyle.Render(m.list.View()) } @@ -350,3 +341,12 @@ func colorGrid(xSteps, ySteps int) [][]string { return grid } + +func execPortKill(m model) { + rgx := regexp.MustCompile(`\((.*?)\)`) + pid := rgx.FindStringSubmatch(m.list.SelectedItem().FilterValue())[1] + killPort(pid) + // Get running processes again when a process is killed + m.list.SetItems(getProcesses()) + m.list.ResetFilter() +} From 0514c41c91969969200395b0f77df5bfa2217352 Mon Sep 17 00:00:00 2001 From: whoop-t Date: Fri, 23 Jun 2023 19:53:08 -0700 Subject: [PATCH 4/6] fix: use pointer in execPortKill --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index be4ae17..253901d 100644 --- a/main.go +++ b/main.go @@ -104,7 +104,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } else { // If accepted killing the port, grab PID + execute killPort() if m.activeButton == "yes" { - execPortKill(m) + execPortKill(&m) } // In all cases, reset selected port at the end m.selectedPort = "" @@ -146,7 +146,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } // If ok is clicked } else if zone.Get("ok").InBounds(msg) { - execPortKill(m) + execPortKill(&m) m.selectedPort = "" // If no is clicked } else if zone.Get("no").InBounds(msg) { @@ -342,7 +342,7 @@ func colorGrid(xSteps, ySteps int) [][]string { return grid } -func execPortKill(m model) { +func execPortKill(m *model) { rgx := regexp.MustCompile(`\((.*?)\)`) pid := rgx.FindStringSubmatch(m.list.SelectedItem().FilterValue())[1] killPort(pid) From e92337f87c48da7383713bca353d56abf9739477 Mon Sep 17 00:00:00 2001 From: whoop-t Date: Sun, 25 Jun 2023 17:07:33 -0700 Subject: [PATCH 5/6] fix: also mark desc area for better ux --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 253901d..38c111c 100644 --- a/main.go +++ b/main.go @@ -70,7 +70,7 @@ type item struct { } func (i item) Title() string { return zone.Mark(i.title, i.title) } -func (i item) Description() string { return i.desc } +func (i item) Description() string { return zone.Mark(i.desc, i.desc) } func (i item) FilterValue() string { return zone.Mark(i.title, i.title) } type model struct { @@ -136,7 +136,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { for i, listItem := range m.list.VisibleItems() { item, _ := listItem.(item) // Check each item to see if it's in bounds. - if zone.Get(item.title).InBounds(msg) { + if zone.Get(item.title).InBounds(msg) || zone.Get(item.desc).InBounds(msg) { // If so, select it in the list. m.list.Select(i) port := m.list.Items()[i].FilterValue() From f090f26f9df9130b311decd3b0cef73df40fe63a Mon Sep 17 00:00:00 2001 From: whoop-t Date: Thu, 6 Jul 2023 21:37:45 -0700 Subject: [PATCH 6/6] feat: add help for mwheel scroll --- main.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/main.go b/main.go index 1bebd13..d86a8c6 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "syscall" "time" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -197,6 +198,21 @@ func main() { m.list.SetStatusBarItemName("process", "processes") //Hide default list title + styles m.list.SetShowTitle(false) + mwheel := + key.NewBinding( + key.WithKeys("mwheel"), + key.WithHelp("mwheel", "up/down"), + ) + m.list.AdditionalShortHelpKeys = func() []key.Binding { + return []key.Binding{ + mwheel, + } + } + m.list.AdditionalFullHelpKeys = func() []key.Binding { + return []key.Binding{ + mwheel, + } + } m.title = initTitle() // Let 'er rip