Browse Source

big change

master
realxlfd 2 months ago
parent
commit
a0265450a2
  1. 3
      .gitignore
  2. 2
      README.MD
  3. 161
      cli/cui/init.go
  4. 38
      cli/cui/tea.go
  5. 2
      cli/input.go
  6. 2
      cli/key.go
  7. 2
      cli/setter.go
  8. 68
      cli/spinner/spinner.go
  9. 0
      cli/spinner/spinner.md
  10. 0
      cli/spinner/styles.go
  11. 2
      cli/ui.go
  12. 15
      go.mod
  13. 32
      go.sum

3
.gitignore

@ -1,2 +1,3 @@
/main.go
/out/
/out/
.idea/*

2
README.MD

@ -7,6 +7,6 @@
## 说明
- [filescanner](utils/file/scanner/filescanner.md)
- [openai](netapis/openai/openai.md)
- [spinner](cmdui/spinner/spinner.md)
- [spinner](cli/spinner/spinner.md)
- [threadpool](utils/threadpool/threadpool.md)
- [strbuilder](utils/strbuilder/strbuilder.md)

161
cli/cui/init.go

@ -0,0 +1,161 @@
package cui
import (
tea "github.com/charmbracelet/bubbletea"
"time"
)
const (
DefaultRefreshRate = 15
)
type Framer interface {
Frame() string
}
type Size struct {
Width int
Height int
}
type UI struct {
// External
WindowResizeListener func(width, height int) // WindowResizeListener 窗口大小改变时的回调函数
// Internal
run bool
frameModel Framer
refreshRate int
refreshInterval time.Duration
render *tea.Program
channels struct {
frame chan string
command chan string
sizeChange chan Size
}
// GoRoutines
Go struct {
resizeListener func()
framer func()
eventListener func()
}
// EventHandler
keyEventHandler map[string]func()
}
// NewUI 创建UI实例
func NewUI(frameModel Framer) (*UI, error) {
// 计算刷新间隔
rInterval := time.Second / DefaultRefreshRate
ui := &UI{
frameModel: frameModel,
WindowResizeListener: nil,
refreshRate: DefaultRefreshRate,
refreshInterval: rInterval,
render: nil,
keyEventHandler: make(map[string]func()),
}
ui.channels.frame = make(chan string)
ui.channels.command = make(chan string)
ui.channels.sizeChange = make(chan Size)
// 初始化渲染器
ui.render = tea.NewProgram(
teaModel{
ui: ui,
},
)
// 初始化渲染输出函数
frameRender := func() {
for ui.run {
ui.channels.frame <- ui.frameModel.Frame()
}
ui.Go.framer = nil
}
// 设置窗口大小变化监听器
sizeChangeListenerRouting := func() {
for ui.run {
select {
case size := <-ui.channels.sizeChange:
if ui.WindowResizeListener != nil {
ui.WindowResizeListener(size.Width, size.Height)
}
}
}
ui.Go.resizeListener = nil
}
eventListener := func() {
for ui.run {
select {
case key := <-ui.channels.command:
f := ui.keyEventHandler[key]
if f != nil {
f()
}
}
}
}
// GoRoutine 绑定
ui.Go.eventListener = eventListener
ui.Go.framer = frameRender
ui.Go.resizeListener = sizeChangeListenerRouting
return ui, nil
}
// Run 启动UI
func (ui *UI) Run() *UI {
if ui.run {
return ui
}
ui.run = true
// 启动线程
go ui.Go.framer()
go ui.Go.resizeListener()
go ui.Go.eventListener()
var err error
_, err = ui.render.Run()
if err != nil {
panic(`internal render error`)
}
return ui
}
// SetFramer 设置渲染器
func (ui *UI) SetFramer(framer Framer) *UI {
if framer == nil {
panic(`assigned a nil to ui.framer`)
}
ui.frameModel = framer
return ui
}
// SetFPS 设置刷新率
func (ui *UI) SetFPS(rate int) *UI {
if rate <= 0 {
panic(`invalid fps rate`)
}
ui.refreshRate = rate
ui.refreshInterval = time.Second / time.Duration(rate)
return ui
}
// Bind 绑定键盘事件监听器
func (ui *UI) Bind(f func(), keys ...string) {
if ui.keyEventHandler == nil {
return
}
for _, key := range keys {
ui.keyEventHandler[key] = f
}
}
// RemoveBindings 移除键盘事件监听器
func (ui *UI) RemoveBindings(keys ...string) {
for _, key := range keys {
ui.keyEventHandler[key] = nil
}
}
// RemoveAllBindings 移除所有键盘事件监听器
func (ui *UI) RemoveAllBindings() {
ui.keyEventHandler = make(map[string]func())
}

38
cli/cui/tea.go

@ -0,0 +1,38 @@
package cui
import (
tea "github.com/charmbracelet/bubbletea"
"time"
)
type teaModel struct {
ui *UI
}
func (t teaModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch m := msg.(type) {
case tea.KeyMsg:
t.ui.channels.command <- m.String()
case tea.WindowSizeMsg:
t.ui.channels.sizeChange <- Size{
Width: m.Width,
Height: m.Height,
}
case int:
return t, func() tea.Msg {
time.Sleep(t.ui.refreshInterval)
return 0
}
}
return t, nil
}
func (t teaModel) View() string {
return <-t.ui.channels.frame
}
func (t teaModel) Init() tea.Cmd {
return func() tea.Msg {
return 0
}
}

2
cmdui/input.go → cli/input.go

@ -1,4 +1,4 @@
package cmdui
package cli
import (
"bufio"

2
cmdui/key.go → cli/key.go

@ -1,4 +1,4 @@
package cmdui
package cli
import (
str "git.realxlfd.cc/RealXLFD/golib/utils/strbuilder"

2
cmdui/setter.go → cli/setter.go

@ -1,4 +1,4 @@
package cmdui
package cli
import "fmt"

68
cmdui/spinner/spinner.go → cli/spinner/spinner.go

@ -34,6 +34,7 @@ type Spinner struct {
animeIndex int
length int
msgCh chan string
show bool
}
func (s *Spinner) updateLength() *Spinner {
@ -63,6 +64,7 @@ func (s *Spinner) SetMsg(msg string) *Spinner {
return s.updateLength()
}
// SetSuffix 设置spinner的后缀
func (s *Spinner) SetSuffix(suffix string) *Spinner {
s.suffix = suffix
return s.updateLength()
@ -98,50 +100,64 @@ func (s *Spinner) Start() *Spinner {
5,
)
s.run = true
go s.show()
go s.frame()
return s
}
// Print 打印消息
func (s *Spinner) Print(msg string) {
if s.run {
if s.run && s.show {
s.msgCh <- msg
}
}
func (s *Spinner) show() *Spinner {
// Tick 获取下一个动画帧
func (s *Spinner) Tick() string {
if s.run {
return cyan(s.next())
}
return ""
}
func (s *Spinner) frame() *Spinner {
go func() {
for s.run {
s.animeIndex = (s.animeIndex + 1) % len(s.mode)
time.Sleep(s.delay)
}
}()
if !s.show {
return s
}
for s.run {
select {
case msg := <-s.msgCh:
if s.show {
select {
case msg := <-s.msgCh:
strBuilder := strings.Builder{}
strBuilder.WriteRune('\r')
strBuilder.WriteString(msg)
if len(msg) < s.length {
strBuilder.WriteString(
strings.Repeat(
" ",
s.length-len(msg),
),
)
strBuilder.WriteString("\n")
}
fmt.Print(strBuilder.String())
default:
}
strBuilder := strings.Builder{}
strBuilder.WriteRune('\r')
strBuilder.WriteString(msg)
if len(msg) < s.length {
strBuilder.WriteString(
strings.Repeat(
" ",
s.length-len(msg),
),
)
strBuilder.WriteString("\n")
}
strBuilder.WriteString(s.suffix)
strBuilder.WriteString(cyan(s.next()))
strBuilder.WriteString(" ")
strBuilder.WriteString(s.msg)
fmt.Print(strBuilder.String())
default:
}
strBuilder := strings.Builder{}
strBuilder.WriteRune('\r')
strBuilder.WriteString(s.suffix)
strBuilder.WriteString(cyan(s.next()))
strBuilder.WriteString(" ")
strBuilder.WriteString(s.msg)
fmt.Print(strBuilder.String())
time.Sleep(UPDATE)
}
time.Sleep(UPDATE)
return s
}
@ -165,7 +181,7 @@ func (s *Spinner) joinMsg(msg ...string) string {
// Finish Spinner显示为完成
func (s *Spinner) Finish(info ...string) {
if !s.run {
if !s.run || !s.show {
return
}
s.msg = s.joinMsg(info...)
@ -181,7 +197,7 @@ func (s *Spinner) Finish(info ...string) {
// Abort 终止Spinner,显示为错误
func (s *Spinner) Abort(info ...string) {
if !s.run {
if !s.run || !s.show {
return
}
s.msg = s.joinMsg(info...)

0
cmdui/spinner/spinner.md → cli/spinner/spinner.md

0
cmdui/spinner/styles.go → cli/spinner/styles.go

2
cmdui/ui.go → cli/ui.go

@ -1,4 +1,4 @@
package cmdui
package cli
import (
"os"

15
go.mod

@ -4,6 +4,7 @@ go 1.22
require (
github.com/PuerkitoBio/goquery v1.9.1
github.com/charmbracelet/bubbletea v0.25.0
github.com/dustin/go-humanize v1.0.1
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/gookit/color v1.5.4
@ -13,14 +14,26 @@ require (
require (
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/moul/http2curl v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/smartystreets/goconvey v1.8.1 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

32
go.sum

@ -2,6 +2,12 @@ github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VP
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
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/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@ -19,18 +25,37 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
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-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
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/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI=
github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
@ -54,6 +79,7 @@ golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -61,7 +87,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/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.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@ -69,6 +97,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
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=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=

Loading…
Cancel
Save