chore(deps): bump to charm.land v2 dependencies (#161)
Co-authored-by: bjarneo <bjarneo@users.noreply.github.com>
This commit is contained in:
@@ -3,8 +3,8 @@ module cliamp
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
charm.land/bubbletea/v2 v2.0.2
|
||||
charm.land/lipgloss/v2 v2.0.2
|
||||
github.com/devgianlu/go-librespot v0.7.1
|
||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
|
||||
github.com/godbus/dbus/v5 v5.2.2
|
||||
@@ -23,14 +23,17 @@ require (
|
||||
cloud.google.com/go/auth v0.18.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // 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/coder/websocket v1.8.14 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/devgianlu/shannon v0.0.0-20230613115856-82ec90b7fa7e // indirect
|
||||
@@ -38,7 +41,6 @@ require (
|
||||
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17 // indirect
|
||||
github.com/ebitengine/oto/v3 v3.3.2 // indirect
|
||||
github.com/ebitengine/purego v0.8.0 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -52,15 +54,11 @@ require (
|
||||
github.com/icza/bitio v1.1.0 // indirect
|
||||
github.com/jfreymuth/pulse v0.1.2-0.20241102120944-4ffb35054b53 // indirect
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.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.16 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mewkiz/flac v1.0.12 // indirect
|
||||
github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
@@ -77,7 +75,8 @@ require (
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
||||
google.golang.org/grpc v1.79.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
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.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
|
||||
charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
|
||||
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
@@ -6,26 +10,32 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
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.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
|
||||
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
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/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
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/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
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/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/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/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
@@ -47,8 +57,6 @@ github.com/ebitengine/oto/v3 v3.3.2 h1:VTWBsKX9eb+dXzaF4jEwQbs4yWIdXukJ0K40KgkpY
|
||||
github.com/ebitengine/oto/v3 v3.3.2/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
|
||||
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
|
||||
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -96,26 +104,18 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
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/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/madelynnblue/go-dsp v1.0.0 h1:ufzvSGl8IdjCA6BFVUx1cZW/aDiiXxDBWU1MpkrtAiM=
|
||||
github.com/madelynnblue/go-dsp v1.0.0/go.mod h1:dpf07Rj/u3te6cW3KwRBAqlyjP4InXHhNaYVuY73hHU=
|
||||
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.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
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/mewkiz/flac v1.0.12 h1:5Y1BRlUebfiVXPmz7hDD7h3ceV2XNrGNMejNVjDpgPY=
|
||||
github.com/mewkiz/flac v1.0.12/go.mod h1:1UeXlFRJp4ft2mfZnPLRpQTd7cSjb/s17o7JQzzyrCA=
|
||||
github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14 h1:tnAPMExbRERsyEYkmR1YjhTgDM0iqyiBYf8ojRXxdbA=
|
||||
github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14/go.mod h1:QYCFBiH5q6XTHEbWhR0uhR3M9qNPoD2CSQzr0g75kE4=
|
||||
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/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -124,7 +124,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
@@ -185,15 +184,13 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/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.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/config"
|
||||
"cliamp/external/jellyfin"
|
||||
@@ -270,8 +270,7 @@ func run(overrides config.Overrides, positional []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
prog := tea.NewProgram(m, tea.WithAltScreen())
|
||||
prog.SetWindowTitle(model.InitialTerminalTitle())
|
||||
prog := tea.NewProgram(m)
|
||||
|
||||
if luaMgr != nil {
|
||||
luaMgr.SetControlProvider(luaplugin.ControlProvider{
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/lyrics"
|
||||
"cliamp/player"
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/player"
|
||||
"cliamp/ui"
|
||||
"cliamp/playlist"
|
||||
"cliamp/resolve"
|
||||
"cliamp/ui"
|
||||
)
|
||||
|
||||
// fbMaxVisible is a number of visible entries in file browser.
|
||||
@@ -120,7 +120,7 @@ func (m *Model) loadFBDir() {
|
||||
}
|
||||
|
||||
// handleFileBrowserKey processes key presses while the file browser is open.
|
||||
func (m *Model) handleFileBrowserKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleFileBrowserKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
var cd string
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
@@ -134,7 +134,7 @@ func (m *Model) handleFileBrowserKey(msg tea.KeyMsg) tea.Cmd {
|
||||
if m.fileBrowser.cursor > 0 {
|
||||
m.fileBrowser.cursor--
|
||||
} else if len(m.fileBrowser.entries) > 0 {
|
||||
m.fileBrowser.cursor = len(m.fileBrowser.entries)-1
|
||||
m.fileBrowser.cursor = len(m.fileBrowser.entries) - 1
|
||||
}
|
||||
|
||||
case "down", "j":
|
||||
@@ -151,7 +151,7 @@ func (m *Model) handleFileBrowserKey(msg tea.KeyMsg) tea.Cmd {
|
||||
|
||||
case "pgdown", "ctrl+d":
|
||||
if m.fileBrowser.cursor < len(m.fileBrowser.entries)-1 {
|
||||
m.fileBrowser.cursor = min(len(m.fileBrowser.entries)-1, m.fileBrowser.cursor + fbMaxVisible)
|
||||
m.fileBrowser.cursor = min(len(m.fileBrowser.entries)-1, m.fileBrowser.cursor+fbMaxVisible)
|
||||
}
|
||||
|
||||
case "enter", "l", "right":
|
||||
@@ -203,7 +203,7 @@ func (m *Model) handleFileBrowserKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.loadFBDir()
|
||||
}
|
||||
|
||||
case " ":
|
||||
case "space":
|
||||
if m.fileBrowser.cursor < len(m.fileBrowser.entries) {
|
||||
e := m.fileBrowser.entries[m.fileBrowser.cursor]
|
||||
if !e.isParent && (e.isAudio || e.isDir) {
|
||||
@@ -279,9 +279,9 @@ func (m *Model) fbConfirm(replace bool) tea.Cmd {
|
||||
|
||||
// renderFileBrowser renders the file browser overlay.
|
||||
func (m Model) renderFileBrowser() string {
|
||||
lines := append(make([]string, 0, 3 + fbMaxVisible + 4),
|
||||
lines := append(make([]string, 0, 3+fbMaxVisible+4),
|
||||
titleStyle.Render("O P E N F I L E S"),
|
||||
dimStyle.Render(" " + m.fileBrowser.dir),
|
||||
dimStyle.Render(" "+m.fileBrowser.dir),
|
||||
"",
|
||||
)
|
||||
|
||||
@@ -353,7 +353,7 @@ func (m Model) renderFileBrowser() string {
|
||||
}
|
||||
}
|
||||
|
||||
help := helpKey("↑↓", "Scroll ") + helpKey("Enter", "Open ") +
|
||||
help := helpKey("↑↓", "Scroll ") + helpKey("Enter", "Open ") +
|
||||
helpKey("Spc", "Select ") + helpKey("a", "All ") +
|
||||
helpKey("←", "Back ") + helpKey("~.", "Home/Cwd ")
|
||||
if os.PathSeparator == '\\' {
|
||||
|
||||
+2
-5
@@ -4,7 +4,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/luaplugin"
|
||||
"cliamp/player"
|
||||
@@ -160,10 +160,7 @@ func (m Model) Init() tea.Cmd {
|
||||
if m.luaMgr != nil {
|
||||
m.luaMgr.Emit(luaplugin.EventAppStart, nil)
|
||||
}
|
||||
cmds := []tea.Cmd{tickCmd(), tea.WindowSize()}
|
||||
if cmd := m.terminalTitleCmd(); cmd != nil {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
cmds := []tea.Cmd{tickCmd(), func() tea.Msg { return tea.RequestWindowSize() }}
|
||||
if m.provider != nil {
|
||||
cmds = append(cmds, fetchPlaylistsCmd(m.provider))
|
||||
}
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
)
|
||||
|
||||
// keymapEntry is a key-action pair for the keymap overlay.
|
||||
@@ -62,8 +62,8 @@ var keymapEntries = []keymapEntry{
|
||||
}
|
||||
|
||||
// handleKeymapKey processes key presses while the keymap overlay is open.
|
||||
func (m *Model) handleKeymapKey(msg tea.KeyMsg) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handleKeymapKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.keymap.visible = false
|
||||
m.keymap.search = ""
|
||||
@@ -78,7 +78,7 @@ func (m *Model) handleKeymapKey(msg tea.KeyMsg) tea.Cmd {
|
||||
count = len(m.keymap.filtered)
|
||||
}
|
||||
if count > 0 {
|
||||
m.keymap.cursor = count-1
|
||||
m.keymap.cursor = count - 1
|
||||
}
|
||||
}
|
||||
case tea.KeyDown:
|
||||
@@ -105,8 +105,8 @@ func (m *Model) handleKeymapKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.keymap.visible = false
|
||||
return m.quit()
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.keymap.search += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.keymap.search += msg.Text
|
||||
m.updateKeymapFilter()
|
||||
}
|
||||
}
|
||||
|
||||
+45
-43
@@ -8,14 +8,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"cliamp/config"
|
||||
"cliamp/ui"
|
||||
"cliamp/internal/fileutil"
|
||||
"cliamp/playlist"
|
||||
"cliamp/provider"
|
||||
"cliamp/ui"
|
||||
)
|
||||
|
||||
// quit shuts down the player and signals the TUI to exit.
|
||||
@@ -47,7 +47,7 @@ func (m *Model) scrobbleCurrent() {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) handleSpeedKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleSpeedKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.String() {
|
||||
case "q", "ctrl+c":
|
||||
return m.quit()
|
||||
@@ -63,14 +63,14 @@ func (m *Model) handleSpeedKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
case "esc", "backspace":
|
||||
m.focus = focusEQ
|
||||
case " ":
|
||||
case "space":
|
||||
return m.togglePlayPause()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleKey processes a single key press and returns an optional command.
|
||||
func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
if m.keymap.visible {
|
||||
return m.handleKeymapKey(msg)
|
||||
}
|
||||
@@ -175,7 +175,7 @@ func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd {
|
||||
} else if len(m.providerLists) > 0 {
|
||||
m.provCursor = len(m.providerLists) - 1
|
||||
}
|
||||
case " ":
|
||||
case "space":
|
||||
return m.togglePlayPause()
|
||||
case "down", "j":
|
||||
if m.provCursor < len(m.providerLists)-1 {
|
||||
@@ -269,7 +269,7 @@ func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.focus = focusPlaylist
|
||||
case "esc", "backspace":
|
||||
m.focus = focusSpeed
|
||||
case " ":
|
||||
case "space":
|
||||
return m.togglePlayPause()
|
||||
}
|
||||
return nil
|
||||
@@ -287,7 +287,7 @@ func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.focus = focusProvider
|
||||
}
|
||||
|
||||
case " ":
|
||||
case "space":
|
||||
cmd := m.togglePlayPause()
|
||||
m.notifyMPRIS()
|
||||
return cmd
|
||||
@@ -608,8 +608,10 @@ func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.fullVis = !m.fullVis
|
||||
if m.fullVis {
|
||||
m.vis.Rows = max(ui.DefaultVisRows, (m.height-10)*4/5)
|
||||
ui.PanelWidth = max(0, m.width-2*ui.PaddingH)
|
||||
} else {
|
||||
m.vis.Rows = ui.DefaultVisRows
|
||||
ui.PanelWidth = max(0, min(m.width, 80)-2*ui.PaddingH)
|
||||
}
|
||||
|
||||
case "x":
|
||||
@@ -712,14 +714,14 @@ func (m *Model) closeJumpMode() {
|
||||
}
|
||||
|
||||
// handleJumpKey processes key presses while in jump-time mode.
|
||||
func (m *Model) handleJumpKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleJumpKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
m.closeJumpMode()
|
||||
return m.quit()
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.closeJumpMode()
|
||||
return nil
|
||||
@@ -745,8 +747,8 @@ func (m *Model) handleJumpKey(msg tea.KeyMsg) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.jumpInput += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.jumpInput += msg.Text
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -754,12 +756,12 @@ func (m *Model) handleJumpKey(msg tea.KeyMsg) tea.Cmd {
|
||||
// handleProvSearchKey processes key presses while filtering the provider playlist list.
|
||||
// For the radio provider, Enter fires an API search; for others, Enter loads the
|
||||
// selected result. Esc cancels and restores the normal catalog view.
|
||||
func (m *Model) handleProvSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleProvSearchKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
// Catalog search: API-based search (no live client-side filtering).
|
||||
if cs, ok := m.provider.(provider.CatalogSearcher); ok {
|
||||
return m.handleCatalogSearchKey(msg, cs)
|
||||
}
|
||||
switch msg.Type {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.provSearch.active = false
|
||||
case tea.KeyEnter:
|
||||
@@ -787,8 +789,8 @@ func (m *Model) handleProvSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.provSearch.query += " "
|
||||
m.updateProvSearch()
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.provSearch.query += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.provSearch.query += msg.Text
|
||||
m.updateProvSearch()
|
||||
}
|
||||
}
|
||||
@@ -797,8 +799,8 @@ func (m *Model) handleProvSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
|
||||
// handleCatalogSearchKey handles search input for providers with catalog search.
|
||||
// Types a query, Enter fires API search, Esc cancels/clears.
|
||||
func (m *Model) handleCatalogSearchKey(msg tea.KeyMsg, cs provider.CatalogSearcher) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handleCatalogSearchKey(msg tea.KeyPressMsg, cs provider.CatalogSearcher) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.provSearch.active = false
|
||||
m.restoreCatalog(cs)
|
||||
@@ -817,8 +819,8 @@ func (m *Model) handleCatalogSearchKey(msg tea.KeyMsg, cs provider.CatalogSearch
|
||||
case tea.KeySpace:
|
||||
m.provSearch.query += " "
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.provSearch.query += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.provSearch.query += msg.Text
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -868,7 +870,7 @@ func (m *Model) toggleExpandPlaylist() {
|
||||
m.adjustScroll()
|
||||
}
|
||||
|
||||
func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleSearchKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
// Allow opening overlays during search (ctrl combos don't conflict with text input).
|
||||
switch msg.String() {
|
||||
case "ctrl+k":
|
||||
@@ -876,7 +878,7 @@ func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.search.active = false
|
||||
m.focus = m.prevFocus
|
||||
@@ -925,8 +927,8 @@ func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.updateSearch()
|
||||
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.search.query += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.search.query += msg.Text
|
||||
m.updateSearch()
|
||||
}
|
||||
}
|
||||
@@ -935,14 +937,14 @@ func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleNetSearchKey processes key presses while in net search mode.
|
||||
func (m *Model) handleNetSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNetSearchKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.String() {
|
||||
case "ctrl+k":
|
||||
m.keymap.visible = true
|
||||
return nil
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.netSearch.active = false
|
||||
m.focus = m.prevFocus
|
||||
@@ -968,8 +970,8 @@ func (m *Model) handleNetSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
m.netSearch.query += " "
|
||||
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.netSearch.query += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.netSearch.query += msg.Text
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,8 +979,8 @@ func (m *Model) handleNetSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleURLInputKey processes key presses while in URL input mode.
|
||||
func (m *Model) handleURLInputKey(msg tea.KeyMsg) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handleURLInputKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.urlInputting = false
|
||||
case tea.KeyEnter:
|
||||
@@ -992,15 +994,15 @@ func (m *Model) handleURLInputKey(msg tea.KeyMsg) tea.Cmd {
|
||||
case tea.KeyBackspace:
|
||||
m.urlInput = removeLastRune(m.urlInput)
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.urlInput += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.urlInput += msg.Text
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handlePlaylistManagerKey dispatches keys to the active manager screen.
|
||||
func (m *Model) handlePlaylistManagerKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handlePlaylistManagerKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch m.plManager.screen {
|
||||
case plMgrScreenList:
|
||||
return m.handlePlMgrListKey(msg)
|
||||
@@ -1013,7 +1015,7 @@ func (m *Model) handlePlaylistManagerKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handlePlMgrListKey handles keys on screen 0 (playlist list).
|
||||
func (m *Model) handlePlMgrListKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handlePlMgrListKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
// If waiting for delete confirmation, only accept y/n.
|
||||
if m.plManager.confirmDel {
|
||||
switch msg.String() {
|
||||
@@ -1078,7 +1080,7 @@ func (m *Model) handlePlMgrListKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handlePlMgrTracksKey handles keys on screen 1 (track list inside a playlist).
|
||||
func (m *Model) handlePlMgrTracksKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handlePlMgrTracksKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
m.plManager.visible = false
|
||||
@@ -1157,8 +1159,8 @@ func (m *Model) handlePlMgrTracksKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handlePlMgrNewNameKey handles keys on screen 2 (new playlist name input).
|
||||
func (m *Model) handlePlMgrNewNameKey(msg tea.KeyMsg) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handlePlMgrNewNameKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.plManager.screen = plMgrScreenList
|
||||
case tea.KeyEnter:
|
||||
@@ -1173,8 +1175,8 @@ func (m *Model) handlePlMgrNewNameKey(msg tea.KeyMsg) tea.Cmd {
|
||||
case tea.KeySpace:
|
||||
m.plManager.newName += " "
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.plManager.newName += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.plManager.newName += msg.Text
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -1203,7 +1205,7 @@ func (m *Model) addToPlaylist(name string) {
|
||||
}
|
||||
|
||||
// handleThemeKey processes key presses while the theme picker is open.
|
||||
func (m *Model) handleThemeKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleThemeKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
count := len(m.themes) + 1 // +1 for Default
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
@@ -1234,7 +1236,7 @@ func (m *Model) handleThemeKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleQueueKey processes key presses while the queue manager overlay is open.
|
||||
func (m *Model) handleQueueKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleQueueKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
qLen := m.playlist.QueueLen()
|
||||
|
||||
switch msg.String() {
|
||||
@@ -1284,7 +1286,7 @@ func (m *Model) handleQueueKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleDeviceKey processes key presses while the audio device picker is open.
|
||||
func (m *Model) handleDeviceKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleDeviceKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
m.devicePicker.visible = false
|
||||
|
||||
+13
-15
@@ -1,14 +1,14 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/playlist"
|
||||
"cliamp/provider"
|
||||
)
|
||||
|
||||
// handleNavBrowserKey processes key presses while the provider browser is open.
|
||||
func (m *Model) handleNavBrowserKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavBrowserKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
if m.navBrowser.prov == nil {
|
||||
m.navBrowser.visible = false
|
||||
return nil
|
||||
@@ -43,7 +43,7 @@ func (m *Model) handleNavBrowserKey(msg tea.KeyMsg) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) handleNavMenuKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavMenuKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
const menuItems = 3
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
@@ -104,7 +104,7 @@ func (m *Model) handleNavMenuKey(msg tea.KeyMsg) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) handleNavByAlbumKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavByAlbumKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch m.navBrowser.screen {
|
||||
case navBrowseScreenList:
|
||||
return m.handleNavAlbumListKey(msg, false)
|
||||
@@ -114,7 +114,7 @@ func (m *Model) handleNavByAlbumKey(msg tea.KeyMsg) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) handleNavByArtistKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavByArtistKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch m.navBrowser.screen {
|
||||
case navBrowseScreenList:
|
||||
return m.handleNavArtistListKey(msg)
|
||||
@@ -124,7 +124,7 @@ func (m *Model) handleNavByArtistKey(msg tea.KeyMsg) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) handleNavByArtistAlbumKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavByArtistAlbumKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch m.navBrowser.screen {
|
||||
case navBrowseScreenList:
|
||||
return m.handleNavArtistListKey(msg)
|
||||
@@ -137,7 +137,7 @@ func (m *Model) handleNavByArtistAlbumKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleNavArtistListKey handles the artist list screen.
|
||||
func (m *Model) handleNavArtistListKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavArtistListKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
// Determine effective list length (filtered or full).
|
||||
listLen := len(m.navBrowser.artists)
|
||||
if len(m.navBrowser.searchIdx) > 0 {
|
||||
@@ -197,7 +197,7 @@ func (m *Model) handleNavArtistListKey(msg tea.KeyMsg) tea.Cmd {
|
||||
|
||||
// handleNavAlbumListKey handles the album list screen.
|
||||
// artistAlbums=true means this is the artist's album sub-screen (ArtistAlbum mode), not the global list.
|
||||
func (m *Model) handleNavAlbumListKey(msg tea.KeyMsg, artistAlbums bool) tea.Cmd {
|
||||
func (m *Model) handleNavAlbumListKey(msg tea.KeyPressMsg, artistAlbums bool) tea.Cmd {
|
||||
// Determine effective list length (filtered or full).
|
||||
listLen := len(m.navBrowser.albums)
|
||||
if len(m.navBrowser.searchIdx) > 0 {
|
||||
@@ -278,7 +278,7 @@ func (m *Model) handleNavAlbumListKey(msg tea.KeyMsg, artistAlbums bool) tea.Cmd
|
||||
}
|
||||
|
||||
// handleNavTrackListKey handles the final track-list screen (used by all modes).
|
||||
func (m *Model) handleNavTrackListKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleNavTrackListKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
// Determine effective list length (filtered or full).
|
||||
listLen := len(m.navBrowser.tracks)
|
||||
if len(m.navBrowser.searchIdx) > 0 {
|
||||
@@ -427,10 +427,9 @@ func (m *Model) handleNavTrackListKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleNavSearchKey handles key input while the nav search bar is open.
|
||||
func (m *Model) handleNavSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handleNavSearchKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
// Close the search bar; keep the filter active so the user can act on results.
|
||||
m.navBrowser.searching = false
|
||||
return nil
|
||||
case tea.KeyEnter:
|
||||
@@ -445,9 +444,8 @@ func (m *Model) handleNavSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Printable character — append to query.
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.navBrowser.search += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.navBrowser.search += msg.Text
|
||||
m.navBrowser.cursor = 0
|
||||
m.navBrowser.scroll = 0
|
||||
m.navUpdateSearch()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/provider"
|
||||
)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/provider"
|
||||
)
|
||||
|
||||
// handleSpotSearchKey dispatches key presses to the active provider search screen.
|
||||
func (m *Model) handleSpotSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleSpotSearchKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
m.spotSearch.visible = false
|
||||
@@ -28,8 +28,8 @@ func (m *Model) handleSpotSearchKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleSpotSearchInputKey handles text input for the search query.
|
||||
func (m *Model) handleSpotSearchInputKey(msg tea.KeyMsg) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handleSpotSearchInputKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.spotSearch.visible = false
|
||||
case tea.KeyEnter:
|
||||
@@ -49,15 +49,15 @@ func (m *Model) handleSpotSearchInputKey(msg tea.KeyMsg) tea.Cmd {
|
||||
case tea.KeySpace:
|
||||
m.spotSearch.query += " "
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.spotSearch.query += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.spotSearch.query += msg.Text
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSpotSearchResultsKey handles navigation through search results.
|
||||
func (m *Model) handleSpotSearchResultsKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleSpotSearchResultsKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
count := len(m.spotSearch.results)
|
||||
|
||||
switch msg.String() {
|
||||
@@ -88,7 +88,7 @@ func (m *Model) handleSpotSearchResultsKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleSpotSearchPlaylistKey handles picking a playlist to add to.
|
||||
func (m *Model) handleSpotSearchPlaylistKey(msg tea.KeyMsg) tea.Cmd {
|
||||
func (m *Model) handleSpotSearchPlaylistKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
count := len(m.spotSearch.playlists) + 1 // +1 for "+ New Playlist..."
|
||||
|
||||
switch msg.String() {
|
||||
@@ -136,11 +136,11 @@ func (m *Model) handleSpotSearchPlaylistKey(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
|
||||
// handleSpotSearchNewNameKey handles text input for new playlist name.
|
||||
func (m *Model) handleSpotSearchNewNameKey(msg tea.KeyMsg) tea.Cmd {
|
||||
switch msg.Type {
|
||||
func (m *Model) handleSpotSearchNewNameKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
switch msg.Code {
|
||||
case tea.KeyEscape:
|
||||
m.spotSearch.screen = spotSearchPlaylist
|
||||
m.spotSearch.cursor = len(m.spotSearch.playlists) // back on "+ New Playlist..."
|
||||
m.spotSearch.cursor = len(m.spotSearch.playlists)
|
||||
case tea.KeyEnter:
|
||||
if m.spotSearch.newName != "" && !m.spotSearch.loading {
|
||||
c, cOk := m.spotSearch.prov.(provider.PlaylistCreator)
|
||||
@@ -159,8 +159,8 @@ func (m *Model) handleSpotSearchNewNameKey(msg tea.KeyMsg) tea.Cmd {
|
||||
case tea.KeySpace:
|
||||
m.spotSearch.newName += " "
|
||||
default:
|
||||
if msg.Type == tea.KeyRunes {
|
||||
m.spotSearch.newName += string(msg.Runes)
|
||||
if len(msg.Text) > 0 {
|
||||
m.spotSearch.newName += msg.Text
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+9
-9
@@ -127,14 +127,14 @@ type Model struct {
|
||||
// Provider state
|
||||
provider playlist.Provider
|
||||
localProvider playlist.Provider // local playlist provider for file-based playlist management (always available)
|
||||
providerLists []playlist.PlaylistInfo
|
||||
provCursor int
|
||||
provLoading bool
|
||||
provSignIn bool // true when provider needs interactive sign-in
|
||||
providers []ProviderEntry // all available providers
|
||||
provPillIdx int // selected pill index
|
||||
eqPresetIdx int // -1 = custom, 0+ = index into eqPresets
|
||||
eqCustomLabel string // non-empty = plugin-defined preset label (shown instead of "Custom")
|
||||
providerLists []playlist.PlaylistInfo
|
||||
provCursor int
|
||||
provLoading bool
|
||||
provSignIn bool // true when provider needs interactive sign-in
|
||||
providers []ProviderEntry // all available providers
|
||||
provPillIdx int // selected pill index
|
||||
eqPresetIdx int // -1 = custom, 0+ = index into eqPresets
|
||||
eqCustomLabel string // non-empty = plugin-defined preset label (shown instead of "Custom")
|
||||
|
||||
// Overlay / feature state (see state.go for struct definitions)
|
||||
search searchState
|
||||
@@ -149,7 +149,7 @@ type Model struct {
|
||||
spotSearch spotSearchState
|
||||
fileBrowser fileBrowserState
|
||||
navBrowser navBrowserState
|
||||
catalogBatch catalogBatchState
|
||||
catalogBatch catalogBatchState
|
||||
ytdlBatch ytdlBatchState
|
||||
reconnect reconnectState
|
||||
save saveState
|
||||
|
||||
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/playlist"
|
||||
)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/playlist"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/playlist"
|
||||
"cliamp/provider"
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"cliamp/playlist"
|
||||
"cliamp/ui"
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/ui"
|
||||
)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
|
||||
"cliamp/ui"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
)
|
||||
|
||||
func TestHandleSpeedKeyUsesArrowKeysWhenSpeedFocused(t *testing.T) {
|
||||
@@ -28,7 +27,7 @@ func TestHandleSpeedKeyUsesArrowKeysWhenSpeedFocused(t *testing.T) {
|
||||
focus: focusSpeed,
|
||||
}
|
||||
|
||||
if cmd := m.handleKey(tea.KeyMsg{Type: tea.KeyRight}); cmd != nil {
|
||||
if cmd := m.handleKey(tea.KeyPressMsg{Code: tea.KeyRight}); cmd != nil {
|
||||
t.Fatalf("handleKey(right) cmd = %v, want nil", cmd)
|
||||
}
|
||||
if got := sharedPlayer.Speed(); got != 1.25 {
|
||||
@@ -38,7 +37,7 @@ func TestHandleSpeedKeyUsesArrowKeysWhenSpeedFocused(t *testing.T) {
|
||||
t.Fatalf("speedSaveAfter after right = %v, want %v", got, speedSaveDebounce)
|
||||
}
|
||||
|
||||
if cmd := m.handleKey(tea.KeyMsg{Type: tea.KeyLeft}); cmd != nil {
|
||||
if cmd := m.handleKey(tea.KeyPressMsg{Code: tea.KeyLeft}); cmd != nil {
|
||||
t.Fatalf("handleKey(left) cmd = %v, want nil", cmd)
|
||||
}
|
||||
if got := sharedPlayer.Speed(); got != 1.0 {
|
||||
|
||||
@@ -246,5 +246,4 @@ type terminalTitleState struct {
|
||||
introActive bool
|
||||
introOffset int
|
||||
introTick int
|
||||
last string
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"cliamp/ui"
|
||||
)
|
||||
@@ -34,7 +34,7 @@ var (
|
||||
Bold(true)
|
||||
|
||||
eqInactiveStyle = lipgloss.NewStyle().
|
||||
Foreground(ui.ColorDim)
|
||||
Foreground(ui.ColorDim)
|
||||
|
||||
playlistActiveStyle = lipgloss.NewStyle().
|
||||
Foreground(ui.ColorPlaying).
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package model
|
||||
import (
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/ui"
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"cliamp/playlist"
|
||||
"cliamp/ui"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
)
|
||||
|
||||
var sharedPlayer *player.Player
|
||||
@@ -38,7 +38,7 @@ func TestTickIntervalStoppedUsesSlow(t *testing.T) {
|
||||
player: sharedPlayer,
|
||||
vis: ui.NewVisualizer(float64(sharedPlayer.SampleRate())),
|
||||
playlist: playlist.New(),
|
||||
termTitle: terminalTitleState{last: baseTerminalTitle},
|
||||
termTitle: terminalTitleState{},
|
||||
}
|
||||
|
||||
// Player is stopped by default (IsPlaying=false).
|
||||
@@ -153,7 +153,7 @@ func TestUpdateRequestsVisualizerRefreshWhenOverlayCloses(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
nextModel, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEscape})
|
||||
nextModel, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
|
||||
if cmd != nil {
|
||||
t.Fatalf("Update() cmd = %v, want nil", cmd)
|
||||
}
|
||||
@@ -178,7 +178,7 @@ func TestUpdateRequestsVisualizerRefreshWhenLyricsClose(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
nextModel, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEscape})
|
||||
nextModel, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
|
||||
if cmd != nil {
|
||||
t.Fatalf("Update() cmd = %v, want nil", cmd)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
"cliamp/playlist"
|
||||
)
|
||||
|
||||
@@ -29,10 +27,6 @@ type terminalTitleValues struct {
|
||||
|
||||
var defaultTerminalTitleIntroRunes = []rune(defaultTerminalTitleIntro)
|
||||
|
||||
func InitialTerminalTitle() string {
|
||||
return sanitizeTerminalTitle(titleIntroFrame(titleIntroInitialOffset(titleIntroViewportDefault), titleIntroViewportDefault, defaultTerminalTitleIntroRunes))
|
||||
}
|
||||
|
||||
func initialTerminalTitleState() terminalTitleState {
|
||||
if len(defaultTerminalTitleIntroRunes) == 0 {
|
||||
return terminalTitleState{}
|
||||
@@ -141,15 +135,6 @@ func sanitizeTerminalTitle(title string) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (m *Model) terminalTitleCmd() tea.Cmd {
|
||||
title := currentTerminalTitle(m.termTitle, m.width, m.terminalTitleValues())
|
||||
if title == m.termTitle.last {
|
||||
return nil
|
||||
}
|
||||
m.termTitle.last = title
|
||||
return tea.SetWindowTitle(title)
|
||||
}
|
||||
|
||||
func (m *Model) advanceTerminalTitle() {
|
||||
advanceTerminalTitleState(&m.termTitle, m.width)
|
||||
}
|
||||
|
||||
@@ -116,14 +116,6 @@ func TestTerminalTitleIntroSequence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitialTerminalTitle(t *testing.T) {
|
||||
got := InitialTerminalTitle()
|
||||
want := strings.Repeat(" ", titleIntroViewportDefault-4) + "It r"
|
||||
if got != want {
|
||||
t.Fatalf("InitialTerminalTitle() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentTerminalTitleSanitizesRenderedTitle(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
+4
-6
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"cliamp/config"
|
||||
"cliamp/ipc"
|
||||
@@ -35,7 +35,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}()
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
case tea.KeyPressMsg:
|
||||
cmd := m.handleKey(msg)
|
||||
if m.quitting {
|
||||
return m, tea.Quit
|
||||
@@ -62,9 +62,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
ui.PanelWidth = max(0, frameW-2*ui.PaddingH)
|
||||
if m.fullVis {
|
||||
m.vis.Rows = max(ui.DefaultVisRows, (m.height-10)*4/5)
|
||||
ui.PanelWidth = max(0, m.width-2*ui.PaddingH)
|
||||
}
|
||||
m.plVisible = m.defaultPlVisible()
|
||||
return m, m.terminalTitleCmd()
|
||||
return m, nil
|
||||
|
||||
case seekTickMsg:
|
||||
// Async yt-dlp seek completed.
|
||||
@@ -254,9 +255,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
m.advanceTerminalTitle()
|
||||
if cmd := m.terminalTitleCmd(); cmd != nil {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
cmds = append(cmds, tickCmdAt(m.tickInterval()))
|
||||
return m, tea.Batch(cmds...)
|
||||
|
||||
|
||||
+32
-23
@@ -7,12 +7,13 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"cliamp/playlist"
|
||||
"cliamp/ui"
|
||||
"cliamp/provider"
|
||||
"cliamp/theme"
|
||||
"cliamp/ui"
|
||||
)
|
||||
|
||||
// titleScrollSep is the separator runes for cyclic title scrolling,
|
||||
@@ -38,9 +39,9 @@ func playlistLabel(prefix string, p playlist.PlaylistInfo) string {
|
||||
}
|
||||
|
||||
// View renders the full TUI frame.
|
||||
func (m Model) View() string {
|
||||
func (m Model) View() tea.View {
|
||||
if m.quitting {
|
||||
return ""
|
||||
return tea.NewView("")
|
||||
}
|
||||
|
||||
screen := m.activeScreen()
|
||||
@@ -48,43 +49,51 @@ func (m Model) View() string {
|
||||
m.refreshVisualizerIfPending()
|
||||
}
|
||||
|
||||
var content string
|
||||
switch screen {
|
||||
case screenKeymap:
|
||||
return m.renderKeymapOverlay()
|
||||
content = m.renderKeymapOverlay()
|
||||
case screenThemePicker:
|
||||
return m.renderThemePicker()
|
||||
content = m.renderThemePicker()
|
||||
case screenDevicePicker:
|
||||
return m.renderDeviceOverlay()
|
||||
content = m.renderDeviceOverlay()
|
||||
case screenFileBrowser:
|
||||
return m.renderFileBrowser()
|
||||
content = m.renderFileBrowser()
|
||||
case screenNavBrowser:
|
||||
return m.renderNavBrowser()
|
||||
content = m.renderNavBrowser()
|
||||
case screenPlaylistManager:
|
||||
return m.renderPlaylistManager()
|
||||
content = m.renderPlaylistManager()
|
||||
case screenSpotSearch:
|
||||
return m.renderSpotSearch()
|
||||
content = m.renderSpotSearch()
|
||||
case screenQueue:
|
||||
return m.renderQueueOverlay()
|
||||
content = m.renderQueueOverlay()
|
||||
case screenInfo:
|
||||
return m.renderInfoOverlay()
|
||||
content = m.renderInfoOverlay()
|
||||
case screenSearch:
|
||||
return m.renderSearchOverlay()
|
||||
content = m.renderSearchOverlay()
|
||||
case screenNetSearch:
|
||||
return m.renderNetSearchOverlay()
|
||||
content = m.renderNetSearchOverlay()
|
||||
case screenURLInput:
|
||||
return m.renderURLInputOverlay()
|
||||
content = m.renderURLInputOverlay()
|
||||
case screenLyrics:
|
||||
return m.renderLyricsOverlay()
|
||||
content = m.renderLyricsOverlay()
|
||||
case screenJump:
|
||||
return m.renderJumpOverlay()
|
||||
content = m.renderJumpOverlay()
|
||||
case screenFullVisualizer:
|
||||
return m.renderFullVisualizer()
|
||||
content = m.renderFullVisualizer()
|
||||
default:
|
||||
content = strings.Join(m.mainSections(m.renderPlaylist(), true), "\n")
|
||||
}
|
||||
|
||||
content := strings.Join(m.mainSections(m.renderPlaylist(), true), "\n")
|
||||
frame := ui.FrameStyle.Render(content)
|
||||
rendered := content
|
||||
if screen == screenMain || screen == screenFullVisualizer {
|
||||
rendered = m.centerFrame(ui.FrameStyle.Render(content))
|
||||
}
|
||||
|
||||
return m.centerFrame(frame)
|
||||
view := tea.NewView(rendered)
|
||||
view.AltScreen = true
|
||||
view.WindowTitle = currentTerminalTitle(m.termTitle, m.width, m.terminalTitleValues())
|
||||
return view
|
||||
}
|
||||
|
||||
func trimTrailingEmpty(sections []string) []string {
|
||||
@@ -288,7 +297,7 @@ func (m Model) renderFullVisualizer() string {
|
||||
helpKey("V", "Exit ") + helpKey("v", "Mode:"+m.vis.ModeName()+" ") + helpKey("Spc", "⏯ ") + helpKey("<>", "Trk ") + helpKey("+-", "Vol"),
|
||||
}
|
||||
|
||||
return m.centerOverlay(strings.Join(sections, "\n"))
|
||||
return strings.Join(sections, "\n")
|
||||
}
|
||||
|
||||
func (m Model) renderSeekBar() string {
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"charm.land/lipgloss/v2"
|
||||
"cliamp/playlist"
|
||||
"cliamp/ui"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
func withFrameWidth(t *testing.T, width int) {
|
||||
@@ -51,7 +51,7 @@ func TestMainViewShrinksPlaylistForFooterMessages(t *testing.T) {
|
||||
if got := m.effectivePlaylistVisible(); got != 1 {
|
||||
t.Fatalf("effectivePlaylistVisible() = %d, want 1 with one row left after footer lines", got)
|
||||
}
|
||||
if got := lipgloss.Height(m.View()); got > m.height {
|
||||
if got := lipgloss.Height(m.View().Content); got > m.height {
|
||||
t.Fatalf("View() height = %d, want <= %d after footer lines shrink playlist", got, m.height)
|
||||
}
|
||||
}
|
||||
@@ -142,3 +142,46 @@ func TestRenderNavBrowserIncludesFooterMessages(t *testing.T) {
|
||||
t.Fatalf("renderNavBrowser() missing download footer: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewKeepsOverlayLayoutUnchanged(t *testing.T) {
|
||||
withFrameWidth(t, 80)
|
||||
|
||||
m := Model{
|
||||
width: 80,
|
||||
height: 22,
|
||||
keymap: keymapOverlay{
|
||||
visible: true,
|
||||
},
|
||||
}
|
||||
|
||||
want := m.renderKeymapOverlay()
|
||||
if got := lipgloss.Height(want); got > m.height {
|
||||
t.Fatalf("renderKeymapOverlay() height = %d, want <= %d for test setup", got, m.height)
|
||||
}
|
||||
if got := m.View().Content; got != want {
|
||||
t.Fatalf("View() changed overlay layout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullVisualizerViewFitsTerminalWidth(t *testing.T) {
|
||||
if sharedPlayer == nil {
|
||||
t.Skip("audio hardware unavailable")
|
||||
}
|
||||
withFrameWidth(t, 80)
|
||||
|
||||
sharedPlayer.Stop()
|
||||
|
||||
m := Model{
|
||||
player: sharedPlayer,
|
||||
playlist: playlist.New(),
|
||||
vis: ui.NewVisualizer(float64(sharedPlayer.SampleRate())),
|
||||
width: 80,
|
||||
height: 24,
|
||||
fullVis: true,
|
||||
}
|
||||
m.vis.Mode = ui.VisNone
|
||||
|
||||
if got := lipgloss.Width(m.View().Content); got > m.width {
|
||||
t.Fatalf("View() width = %d, want <= %d in full visualizer mode", got, m.width)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"cliamp/resolve"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
tea "charm.land/bubbletea/v2"
|
||||
)
|
||||
|
||||
const ytdlBatchSize = 100 // items per background batch
|
||||
|
||||
+15
-14
@@ -1,27 +1,29 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"cliamp/theme"
|
||||
"image/color"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"cliamp/theme"
|
||||
)
|
||||
|
||||
// CLIAMP color palette using standard ANSI terminal colors (0-15).
|
||||
// These adapt to the user's terminal theme for consistent appearance.
|
||||
var (
|
||||
ColorTitle lipgloss.TerminalColor = lipgloss.ANSIColor(10) // bright green
|
||||
ColorText lipgloss.TerminalColor = lipgloss.ANSIColor(15) // bright white
|
||||
ColorDim lipgloss.TerminalColor = lipgloss.ANSIColor(7) // white (light gray)
|
||||
ColorAccent lipgloss.TerminalColor = lipgloss.ANSIColor(11) // bright yellow
|
||||
ColorPlaying lipgloss.TerminalColor = lipgloss.ANSIColor(10) // bright green
|
||||
ColorSeekBar lipgloss.TerminalColor = lipgloss.ANSIColor(11) // bright yellow
|
||||
ColorVolume lipgloss.TerminalColor = lipgloss.ANSIColor(2) // green
|
||||
ColorError lipgloss.TerminalColor = lipgloss.ANSIColor(9) // bright red
|
||||
ColorTitle color.Color = lipgloss.ANSIColor(10) // bright green
|
||||
ColorText color.Color = lipgloss.ANSIColor(15) // bright white
|
||||
ColorDim color.Color = lipgloss.ANSIColor(7) // white (light gray)
|
||||
ColorAccent color.Color = lipgloss.ANSIColor(11) // bright yellow
|
||||
ColorPlaying color.Color = lipgloss.ANSIColor(10) // bright green
|
||||
ColorSeekBar color.Color = lipgloss.ANSIColor(11) // bright yellow
|
||||
ColorVolume color.Color = lipgloss.ANSIColor(2) // green
|
||||
ColorError color.Color = lipgloss.ANSIColor(9) // bright red
|
||||
|
||||
// Spectrum gradient: green -> yellow -> red
|
||||
SpectrumLow lipgloss.TerminalColor = lipgloss.ANSIColor(10) // bright green
|
||||
SpectrumMid lipgloss.TerminalColor = lipgloss.ANSIColor(11) // bright yellow
|
||||
SpectrumHigh lipgloss.TerminalColor = lipgloss.ANSIColor(9) // bright red
|
||||
SpectrumLow color.Color = lipgloss.ANSIColor(10) // bright green
|
||||
SpectrumMid color.Color = lipgloss.ANSIColor(11) // bright yellow
|
||||
SpectrumHigh color.Color = lipgloss.ANSIColor(9) // bright red
|
||||
)
|
||||
|
||||
// PaddingH is the horizontal padding inside the frame.
|
||||
@@ -51,7 +53,6 @@ var FrameStyle = lipgloss.NewStyle().
|
||||
// If the theme is the default (empty hex values), ANSI fallback colors are restored.
|
||||
func ApplyThemeColors(t theme.Theme) {
|
||||
if t.IsDefault() {
|
||||
// Restore ANSI defaults.
|
||||
ColorTitle = lipgloss.ANSIColor(10)
|
||||
ColorText = lipgloss.ANSIColor(15)
|
||||
ColorDim = lipgloss.ANSIColor(7)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"charm.land/lipgloss/v2"
|
||||
)
|
||||
|
||||
const classicPeakTestGlyphs = "⎺⎻⎼⎽"
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/madelynnblue/go-dsp/fft"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user