diff --git a/component/textbox/textbox.go b/component/textbox/textbox.go new file mode 100644 index 0000000..e5b407b --- /dev/null +++ b/component/textbox/textbox.go @@ -0,0 +1,62 @@ +package textbox + +import ( + ui "github.com/gizak/termui/v3" + "github.com/sqshq/sampler/component" + "github.com/sqshq/sampler/config" + "github.com/sqshq/sampler/console" + "github.com/sqshq/sampler/data" + "image" +) + +type TextBox struct { + *ui.Block + *data.Consumer + alert *data.Alert + text string + border bool +} + +func NewTextBox(c config.TextBoxConfig, palette console.Palette) *TextBox { + + box := TextBox{ + Block: component.NewBlock(c.Title, *c.Border, palette), + Consumer: data.NewConsumer(), + } + + go func() { + for { + select { + case sample := <-box.SampleChannel: + box.text = sample.Value + case alert := <-box.AlertChannel: + box.alert = alert + } + } + }() + + return &box +} + +func (t *TextBox) Draw(buffer *ui.Buffer) { + + t.Block.Draw(buffer) + + cells := ui.ParseStyles(t.text, ui.Theme.Paragraph.Text) + cells = ui.WrapCells(cells, uint(t.Inner.Dx())) + + rows := ui.SplitCells(cells, '\n') + + for y, row := range rows { + if y+t.Inner.Min.Y >= t.Inner.Max.Y { + break + } + row = ui.TrimCells(row, t.Inner.Dx()) + for _, cx := range ui.BuildCellWithXArray(row) { + x, cell := cx.X, cx.Cell + buffer.SetCell(cell, image.Pt(x, y+1).Add(t.Inner.Min)) + } + } + + component.RenderAlert(t.alert, t.Rectangle, buffer) +} diff --git a/config.yml b/config.yml index 80eb868..c2e9fd5 100644 --- a/config.yml +++ b/config.yml @@ -27,30 +27,30 @@ runcharts: scale: 0 items: - label: ACTIVE - init: ${mongoconnection} + init: $mongoconnection sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() - label: INACTIVE - init: ${mongoconnection} + init: $mongoconnection sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() barcharts: - title: EVENTS BY STATUS - position: [[0, 17], [27, 12]] + position: [[0, 17], [28, 12]] rate-ms: 300 scale: 0 items: - label: NEW - init: ${mongoconnection} + init: $mongoconnection sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() - label: TRIGGERED - init: ${mongoconnection} + init: $mongoconnection sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() - label: IN_PROCESS sample: echo 0 - label: FAILED - init: ${mongoconnection} + init: $mongoconnection sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() - label: FINISHED - init: ${mongoconnection} + init: $mongoconnection sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() gauges: - title: YEAR PROGRESS @@ -91,6 +91,31 @@ gauges: sample: echo 60 min: sample: echo 0 +sparklines: + - title: CPU usage + position: [[28, 22], [24, 7]] + scale: 0 + sample: ps -A -o %cpu | awk '{s+=$1} END {print s}' + - title: Memory pages free + position: [[28, 17], [24, 5]] + scale: 0 + sample: memory_pressure | grep 'Pages free' | awk '{print $3}' +textboxes: + - title: Local weather + position: [[0, 30], [13, 7]] + rate-ms: 10000 + sample: curl wttr.in?0ATQF + border: false + - title: New York weather + position: [[8, 30], [13, 7]] + rate-ms: 10000 + sample: curl wttr.in/newyork?0ATQF + border: false + - title: San Francisco weather + position: [[17, 30], [13, 7]] + rate-ms: 10000 + sample: curl wttr.in/sanfrancisco?0ATQF + border: false asciiboxes: - title: LOCAL TIME position: [[53, 17], [27, 5]] @@ -99,12 +124,3 @@ asciiboxes: position: [[53, 22], [27, 7]] sample: env TZ=UTC date +%r font: 3d -sparklines: -- title: CPU usage - position: [[27, 22], [25, 7]] - scale: 0 - sample: ps -A -o %cpu | awk '{s+=$1} END {print s}' -- title: Memory pages free - position: [[27, 17], [25, 5]] - scale: 0 - sample: memory_pressure | grep 'Pages free' | awk '{print $3}' diff --git a/config/component.go b/config/component.go index 320e28a..2e19aa8 100644 --- a/config/component.go +++ b/config/component.go @@ -70,9 +70,16 @@ type BarChartConfig struct { type AsciiBoxConfig struct { ComponentConfig `yaml:",inline"` Item `yaml:",inline"` + Border *bool `yaml:"border,omitempty"` Font *console.AsciiFont `yaml:"font,omitempty"` } +type TextBoxConfig struct { + ComponentConfig `yaml:",inline"` + Item `yaml:",inline"` + Border *bool `yaml:"border,omitempty"` +} + type RunChartConfig struct { ComponentConfig `yaml:",inline"` Legend *LegendConfig `yaml:"legend,omitempty"` diff --git a/config/config.go b/config/config.go index 34327fd..bc2df34 100644 --- a/config/config.go +++ b/config/config.go @@ -16,8 +16,9 @@ type Config struct { RunCharts []RunChartConfig `yaml:"runcharts,omitempty"` BarCharts []BarChartConfig `yaml:"barcharts,omitempty"` Gauges []GaugeConfig `yaml:"gauges,omitempty"` - AsciiBoxes []AsciiBoxConfig `yaml:"asciiboxes,omitempty"` SparkLines []SparkLineConfig `yaml:"sparklines,omitempty"` + TextBoxes []TextBoxConfig `yaml:"textboxes,omitempty"` + AsciiBoxes []AsciiBoxConfig `yaml:"asciiboxes,omitempty"` } func Load() (Config, Options) { @@ -66,16 +67,22 @@ func (c *Config) findComponent(componentType ComponentType, componentTitle strin return &c.Gauges[i].ComponentConfig } } + case TypeSparkLine: + for i, component := range c.SparkLines { + if component.Title == componentTitle { + return &c.SparkLines[i].ComponentConfig + } + } case TypeAsciiBox: for i, component := range c.AsciiBoxes { if component.Title == componentTitle { return &c.AsciiBoxes[i].ComponentConfig } } - case TypeSparkLine: - for i, component := range c.SparkLines { + case TypeTextBox: + for i, component := range c.TextBoxes { if component.Title == componentTitle { - return &c.SparkLines[i].ComponentConfig + return &c.TextBoxes[i].ComponentConfig } } } diff --git a/config/default.go b/config/default.go index 314ecf2..c27eaa8 100644 --- a/config/default.go +++ b/config/default.go @@ -120,6 +120,27 @@ func (c *Config) setDefaultValues() { } c.AsciiBoxes[i] = box } + + for i, box := range c.TextBoxes { + + setDefaultTriggersValues(box.Triggers) + box.ComponentConfig.Type = TypeTextBox + + if box.RateMs == nil { + r := defaultRateMs + box.RateMs = &r + } + if box.Label == nil { + label := string(i) + box.Label = &label + } + if box.Border == nil { + border := true + box.Border = &border + } + + c.TextBoxes[i] = box + } } func setDefaultTriggersValues(triggers []TriggerConfig) { diff --git a/main.go b/main.go index a29f242..aedbc44 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/sqshq/sampler/component/layout" "github.com/sqshq/sampler/component/runchart" "github.com/sqshq/sampler/component/sparkline" + "github.com/sqshq/sampler/component/textbox" "github.com/sqshq/sampler/config" "github.com/sqshq/sampler/console" "github.com/sqshq/sampler/data" @@ -58,11 +59,6 @@ func main() { starter.start(cpt, cpt.Consumer, c.ComponentConfig, []config.Item{c.Item}, c.Triggers) } - for _, c := range cfg.AsciiBoxes { - cpt := asciibox.NewAsciiBox(c, palette) - starter.start(cpt, cpt.Consumer, c.ComponentConfig, []config.Item{c.Item}, c.Triggers) - } - for _, c := range cfg.BarCharts { cpt := barchart.NewBarChart(c, palette) starter.start(cpt, cpt.Consumer, c.ComponentConfig, c.Items, c.Triggers) @@ -73,6 +69,16 @@ func main() { starter.start(cpt, cpt.Consumer, c.ComponentConfig, []config.Item{c.Cur, c.Min, c.Max}, c.Triggers) } + for _, c := range cfg.AsciiBoxes { + cpt := asciibox.NewAsciiBox(c, palette) + starter.start(cpt, cpt.Consumer, c.ComponentConfig, []config.Item{c.Item}, c.Triggers) + } + + for _, c := range cfg.TextBoxes { + cpt := textbox.NewTextBox(c, palette) + starter.start(cpt, cpt.Consumer, c.ComponentConfig, []config.Item{c.Item}, c.Triggers) + } + handler := event.NewHandler(lout, opt) handler.HandleEvents() }