diff --git a/go.mod b/go.mod index 1aa46f9..07d3358 100644 --- a/go.mod +++ b/go.mod @@ -2,20 +2,29 @@ module vdomtest go 1.22.4 -require github.com/wavetermdev/waveterm v0.9.1 +require ( + github.com/shirou/gopsutil/v3 v3.24.5 + github.com/wavetermdev/waveterm v0.9.1 +) require ( github.com/alexflint/go-filemutex v1.3.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/stretchr/testify v1.9.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/wavetermdev/htmltoken v0.2.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/net v0.29.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/term v0.25.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/term v0.26.0 // indirect ) replace github.com/wavetermdev/waveterm => ../waveterm diff --git a/go.sum b/go.sum index 0b3a622..3c67789 100644 --- a/go.sum +++ b/go.sum @@ -5,29 +5,55 @@ 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/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/wavetermdev/htmltoken v0.2.0 h1:sFVPPemlDv7/jg7n4Hx1AEF2m9MVAFjFpELWfhi/DlM= github.com/wavetermdev/htmltoken v0.2.0/go.mod h1:5FM0XV6zNYiNza2iaTcFGj+hnMtgqumFHO31Z8euquk= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/toolbar/style.css b/toolbar/style.css new file mode 100644 index 0000000..65ccb34 --- /dev/null +++ b/toolbar/style.css @@ -0,0 +1,58 @@ +.toolbar { + --low-usage: var(--term-bright-green); + --medium-usage: var(--term-bright-yellow); + --high-usage: var(--term-bright-red); + --text-color: var(--term-cmdtext); + --secondary-text: var(--term-gray); + --divider-color: var(--term-bright-black); +} + +.toolbar { + height: 1.5em; + line-height: 1.5em; + padding: 0 0.5em; + display: flex; + align-items: center; + font-family: system-ui, -apple-system, sans-serif; + gap: 1em; + color: var(--text-color); +} + +.metric-group { + display: flex; + align-items: center; + gap: 0.5em; +} + +.label { + font-size: 0.8em; + color: var(--secondary-text); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.value { + font-size: 0.9em; + min-width: 3em; + color: var(--term-foreground); +} + +.progress-container { + width: 4em; + height: 0.5em; + background: var(--term-bright-black); + border-radius: 3px; + overflow: hidden; +} + +.progress-bar { + height: 100%; + transition: width 0.3s ease, background-color 0.3s ease; +} + +.divider { + width: 1px; + height: 1em; + background: var(--divider-color); + margin: 0 0.25em; +} \ No newline at end of file diff --git a/toolbar/toolbar-main.go b/toolbar/toolbar-main.go new file mode 100644 index 0000000..25573b4 --- /dev/null +++ b/toolbar/toolbar-main.go @@ -0,0 +1,122 @@ +package main + +import ( + "context" + _ "embed" + "fmt" + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/load" + "github.com/wavetermdev/waveterm/pkg/vdom" + "github.com/wavetermdev/waveterm/pkg/vdom/vdomclient" +) + +//go:embed style.css +var styleCSS []byte + +var AppClient = vdomclient.MakeClient(vdomclient.AppOpts{ + CloseOnCtrlC: true, + GlobalStyles: styleCSS, + TargetToolbar: &vdom.VDomTargetToolbar{Toolbar: true, Height: "1.5em"}, +}) + +type Metrics struct { + CPUPercent float64 + LoadAvg [3]float64 +} + +var App = vdomclient.DefineComponent(AppClient, "App", + func(ctx context.Context, _ any) any { + metrics, setMetrics := vdom.UseState(ctx, Metrics{}) + + // Update metrics every second + vdom.UseEffect(ctx, func() func() { + done := make(chan bool) + + go func() { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-done: + return + case <-ticker.C: + if cpuPercent, err := cpu.Percent(0, false); err == nil && len(cpuPercent) > 0 { + if loadAvg, err := load.Avg(); err == nil { + setMetrics(Metrics{ + CPUPercent: cpuPercent[0], + LoadAvg: [3]float64{loadAvg.Load1, loadAvg.Load5, loadAvg.Load15}, + }) + AppClient.SendAsyncInitiation() + } + } + } + } + }() + + return func() { + close(done) + } + }, []any{}) + + // Helper to get color based on CPU usage + getCPUColor := func(cpu float64) string { + switch { + case cpu >= 80: + return "var(--high-usage)" + case cpu >= 50: + return "var(--medium-usage)" + default: + return "var(--low-usage)" + } + } + + return vdom.E("div", + vdom.Class("toolbar"), + // CPU Section + vdom.E("div", + vdom.Class("metric-group"), + vdom.E("span", + vdom.Class("label"), + "CPU", + ), + vdom.E("div", + vdom.Class("progress-container"), + vdom.E("div", + vdom.Class("progress-bar"), + vdom.PStyle("width", fmt.Sprintf("%d%%", int(metrics.CPUPercent))), + vdom.PStyle("backgroundColor", getCPUColor(metrics.CPUPercent)), + ), + ), + vdom.E("span", + vdom.Class("value"), + fmt.Sprintf("%.1f%%", metrics.CPUPercent), + ), + ), + // Divider + vdom.E("div", vdom.Class("divider")), + // Load Average Section + vdom.E("div", + vdom.Class("metric-group"), + vdom.E("span", + vdom.Class("label"), + "Load", + ), + vdom.E("span", + vdom.Class("value"), + fmt.Sprintf("%.2f %.2f %.2f", + metrics.LoadAvg[0], + metrics.LoadAvg[1], + metrics.LoadAvg[2], + ), + ), + ), + ) + }, +) + +func main() { + AppClient.RunMain() +}