From 9cdac2a3d4ea48ab933406be8205e6daea4d799a Mon Sep 17 00:00:00 2001 From: rkfg Date: Mon, 12 Sep 2022 17:50:10 +0300 Subject: [PATCH 1/7] Add channel age column --- README.md | 1 + config/default.go | 1 + network/models/channel.go | 1 + ui/models/models.go | 1 + ui/models/sort.go | 7 +++++++ ui/views/channels.go | 17 +++++++++++++++++ ui/views/views.go | 16 ++++++++++++++++ 7 files changed, 44 insertions(+) diff --git a/README.md b/README.md index e0aec23..2666935 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ columns = [ "UNSETTLED", # the amount unsettled in the channel "CFEE", # the commit fee "LAST UPDATE", # last update of the channel + # "AGE", # approximate channel age "PRIVATE", # true if channel is private "ID", # the id of the channel # "SCID", # short channel id (BxTxO formatted) diff --git a/config/default.go b/config/default.go index 02e81a9..0fafa7f 100644 --- a/config/default.go +++ b/config/default.go @@ -43,6 +43,7 @@ columns = [ "UNSETTLED", # the amount unsettled in the channel "CFEE", # the commit fee "LAST UPDATE", # last update of the channel + # "AGE", # approximate channel age "PRIVATE", # true if channel is private "ID", # the id of the channel # "SCID", # short channel id (BxTxO formatted) diff --git a/network/models/channel.go b/network/models/channel.go index 4a3e1f2..ffdffdb 100644 --- a/network/models/channel.go +++ b/network/models/channel.go @@ -45,6 +45,7 @@ type Channel struct { TotalAmountReceived int64 UpdatesCount uint64 CSVDelay uint32 + Age uint32 Private bool PendingHTLC []*HTLC LastUpdate *time.Time diff --git a/ui/models/models.go b/ui/models/models.go index 6801622..9ada4fc 100644 --- a/ui/models/models.go +++ b/ui/models/models.go @@ -62,6 +62,7 @@ func (m *Models) RefreshChannels(ctx context.Context) error { if channel != nil && (channel.UpdatesCount < channels[i].UpdatesCount || channel.LastUpdate == nil || channel.LocalPolicy == nil || channel.RemotePolicy == nil) { + channels[i].Age = m.Info.BlockHeight - uint32(channels[i].ID>>40) err := m.network.GetChannelInfo(ctx, channels[i]) if err != nil { return err diff --git a/ui/models/sort.go b/ui/models/sort.go index a989ada..9313fab 100644 --- a/ui/models/sort.go +++ b/ui/models/sort.go @@ -40,6 +40,13 @@ func Float64Sort(a, b float64, o Order) bool { return a > b } +func UInt32Sort(a, b uint32, o Order) bool { + if o == Asc { + return a < b + } + return a > b +} + func UInt64Sort(a, b uint64, o Order) bool { if o == Asc { return a < b diff --git a/ui/views/channels.go b/ui/views/channels.go index b5a0220..701610c 100644 --- a/ui/views/channels.go +++ b/ui/views/channels.go @@ -638,6 +638,23 @@ func NewChannels(cfg *config.View, chans *models.Channels) *Channels { return color.White(opts...)(printer.Sprintf("%7d", val)) }, } + case "AGE": + channels.columns[i] = channelsColumn{ + width: 8, + name: fmt.Sprintf("%10s", columns[i]), + sort: func(order models.Order) models.ChannelsSort { + return func(c1, c2 *netmodels.Channel) bool { + return models.UInt32Sort(c1.Age, c2.Age, order) + } + }, + display: func(c *netmodels.Channel, opts ...color.Option) string { + if c.ID == 0 { + return fmt.Sprintf("%10s", "") + } + return color.White(opts...)(fmt.Sprintf("%10s", FormatAge(c.Age))) + }, + } + default: channels.columns[i] = channelsColumn{ width: 21, diff --git a/ui/views/views.go b/ui/views/views.go index 611d21e..bf4c4ca 100644 --- a/ui/views/views.go +++ b/ui/views/views.go @@ -116,3 +116,19 @@ func ToScid(id uint64) string { return fmt.Sprintf("%dx%dx%d", blocknum, txnum, outnum) } + +func FormatAge(age uint32) string { + if age < 6 { + return fmt.Sprintf("%02dm", age*10) + } + if age < 144 { + return fmt.Sprintf("%02dh", age/6) + } + if age < 4383 { + return fmt.Sprintf("%02dd%02dh", age/144, (age%144)/6) + } + if age < 52596 { + return fmt.Sprintf("%02dm%02dd%02dh", age/4383, (age%4383)/144, (age%144)/6) + } + return fmt.Sprintf("%02dy%02dm%02dd", age/52596, (age%52596)/4383, (age%4383)/144) +} From 76454a420a211dabadca00ed7dfa35ce8e9de5d6 Mon Sep 17 00:00:00 2001 From: rkfg Date: Tue, 13 Sep 2022 02:10:51 +0300 Subject: [PATCH 2/7] Replace the color library, colorize channel age --- go.mod | 2 +- go.sum | 16 +++++---- ui/color/color.go | 80 ++++++++++++++++++++++++-------------------- ui/ui.go | 2 +- ui/views/channels.go | 2 +- ui/views/views.go | 29 ++++++++++++---- 6 files changed, 79 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 143da29..572f660 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.16 require ( github.com/BurntSushi/toml v0.3.1 - github.com/fatih/color v1.7.0 github.com/gofrs/uuid v4.0.0+incompatible + github.com/gookit/color v1.5.2 github.com/jroimartin/gocui v0.4.0 github.com/lightningnetwork/lnd v0.15.0-beta github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect diff --git a/go.sum b/go.sum index d4cb887..ab54b7f 100644 --- a/go.sum +++ b/go.sum @@ -179,7 +179,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fergusstrange/embedded-postgres v1.10.0 h1:YnwF6xAQYmKLAXXrrRx4rHDLih47YJwVPvg8jeKfdNg= github.com/fergusstrange/embedded-postgres v1.10.0/go.mod h1:a008U8/Rws5FtIOTGYDYa7beVWsT3qVKyqExqYYjL+c= @@ -281,6 +280,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= +github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -506,13 +507,11 @@ github.com/masterzen/xmlpath v0.0.0-20140218185901-13f4951698ad/go.mod h1:A0zPC5 github.com/mattn/go-colorable v0.0.6/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -635,16 +634,18 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -661,6 +662,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1134,8 +1137,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/ui/color/color.go b/ui/color/color.go index 60b6926..b1b53d3 100644 --- a/ui/color/color.go +++ b/ui/color/color.go @@ -1,35 +1,41 @@ package color -import "github.com/fatih/color" +import "github.com/gookit/color" type Color color.Color var ( - yellow = color.New(color.FgYellow).SprintFunc() - yellowBold = color.New(color.FgYellow, color.Bold).SprintFunc() - green = color.New(color.FgGreen).SprintFunc() - greenBold = color.New(color.FgGreen, color.Bold).SprintFunc() - greenBg = color.New(color.FgBlack, color.BgGreen).SprintFunc() - magentaBg = color.New(color.FgBlack, color.BgMagenta).SprintFunc() - red = color.New(color.FgRed).SprintFunc() - redBold = color.New(color.FgRed, color.Bold).SprintFunc() - cyan = color.New(color.FgCyan).SprintFunc() - cyanBold = color.New(color.FgCyan, color.Bold).SprintFunc() - cyanBg = color.New(color.BgCyan, color.FgBlack).SprintFunc() - white = color.New().SprintFunc() - whiteBold = color.New(color.Bold).SprintFunc() - blackBg = color.New(color.BgBlack, color.FgWhite).SprintFunc() - black = color.New(color.FgBlack).SprintFunc() + yellow = SprintFunc(color.New(color.FgYellow)) + yellowBold = SprintFunc(color.New(color.FgYellow, color.Bold)) + green = SprintFunc(color.New(color.FgGreen)) + greenBold = SprintFunc(color.New(color.FgGreen, color.Bold)) + greenBg = SprintFunc(color.New(color.FgBlack, color.BgGreen)) + magentaBg = SprintFunc(color.New(color.FgBlack, color.BgMagenta)) + red = SprintFunc(color.New(color.FgRed)) + redBold = SprintFunc(color.New(color.FgRed, color.Bold)) + cyan = SprintFunc(color.New(color.FgCyan)) + cyanBold = SprintFunc(color.New(color.FgCyan, color.Bold)) + cyanBg = SprintFunc(color.New(color.BgCyan, color.FgBlack)) + white = SprintFunc(color.New()) + whiteBold = SprintFunc(color.New(color.Bold)) + blackBg = SprintFunc(color.New(color.BgBlack, color.FgWhite)) + black = SprintFunc(color.New(color.FgBlack)) ) +func SprintFunc(c color.Style) func(args ...interface{}) string { + return func(args ...interface{}) string { + return c.Sprint(args...) + } +} + type Option func(*options) type options struct { - bold bool - bg bool + Bold bool + Bg bool } -func newOptions(opts []Option) options { +func NewOptions(opts []Option) options { options := options{} for i := range opts { if opts[i] == nil { @@ -40,24 +46,24 @@ func newOptions(opts []Option) options { return options } -func Bold(o *options) { o.bold = true } -func Background(o *options) { o.bg = true } +func Bold(o *options) { o.Bold = true } +func Background(o *options) { o.Bg = true } func Yellow(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bold { + options := NewOptions(opts) + if options.Bold { return yellowBold } return yellow } func Green(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bold { + options := NewOptions(opts) + if options.Bold { return greenBold } - if options.bg { + if options.Bg { return greenBg } @@ -65,43 +71,43 @@ func Green(opts ...Option) func(a ...interface{}) string { } func Red(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bold { + options := NewOptions(opts) + if options.Bold { return redBold } return red } func White(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bold { + options := NewOptions(opts) + if options.Bold { return whiteBold } return white } func Cyan(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bold { + options := NewOptions(opts) + if options.Bold { return cyanBold } - if options.bg { + if options.Bg { return cyanBg } return cyan } func Black(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bg { + options := NewOptions(opts) + if options.Bg { return blackBg } return black } func Magenta(opts ...Option) func(a ...interface{}) string { - options := newOptions(opts) - if options.bg { + options := NewOptions(opts) + if options.Bg { return magentaBg } return magentaBg diff --git a/ui/ui.go b/ui/ui.go index f7ee586..c1de162 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -11,7 +11,7 @@ import ( ) func Run(ctx context.Context, app *app.App, sub chan *events.Event) error { - g, err := gocui.NewGui(gocui.OutputNormal) + g, err := gocui.NewGui(gocui.Output256) if err != nil { return err } diff --git a/ui/views/channels.go b/ui/views/channels.go index 701610c..0c14805 100644 --- a/ui/views/channels.go +++ b/ui/views/channels.go @@ -651,7 +651,7 @@ func NewChannels(cfg *config.View, chans *models.Channels) *Channels { if c.ID == 0 { return fmt.Sprintf("%10s", "") } - return color.White(opts...)(fmt.Sprintf("%10s", FormatAge(c.Age))) + return ColorizeAge(c.Age, printer.Sprintf("%10s", FormatAge(c.Age)), opts...) }, } diff --git a/ui/views/views.go b/ui/views/views.go index bf4c4ca..fdc72d6 100644 --- a/ui/views/views.go +++ b/ui/views/views.go @@ -7,8 +7,10 @@ import ( "github.com/pkg/errors" "github.com/edouardparis/lntop/config" + lntcolor "github.com/edouardparis/lntop/ui/color" "github.com/edouardparis/lntop/ui/cursor" "github.com/edouardparis/lntop/ui/models" + "github.com/gookit/color" ) type View interface { @@ -120,15 +122,30 @@ func ToScid(id uint64) string { func FormatAge(age uint32) string { if age < 6 { return fmt.Sprintf("%02dm", age*10) - } - if age < 144 { + } else if age < 144 { return fmt.Sprintf("%02dh", age/6) - } - if age < 4383 { + } else if age < 4383 { return fmt.Sprintf("%02dd%02dh", age/144, (age%144)/6) - } - if age < 52596 { + } else if age < 52596 { return fmt.Sprintf("%02dm%02dd%02dh", age/4383, (age%4383)/144, (age%144)/6) } return fmt.Sprintf("%02dy%02dm%02dd", age/52596, (age%52596)/4383, (age%4383)/144) } + +func ColorizeAge(age uint32, text string, opts ...lntcolor.Option) string { + s := 1.0 + l := 0.5 + if age < 52596 { + s = float64(age) / 52596 + } + if age < 8766 { // increase brightness slightly for 16 color terminals or else it renders as black + l = 0.66 + } + val := color.HSL(22./360, s, l).C256().Value() + c := color.S256(val) + options := lntcolor.NewOptions(opts) + if options.Bold { + c.AddOpts(color.Bold) + } + return c.Sprint(text) +} From 66b0b3e1343fb8f42972c35b4a393378df6ebed3 Mon Sep 17 00:00:00 2001 From: rkfg Date: Tue, 13 Sep 2022 02:37:53 +0300 Subject: [PATCH 3/7] Better color gradients for channel age --- ui/views/views.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/ui/views/views.go b/ui/views/views.go index fdc72d6..bfde012 100644 --- a/ui/views/views.go +++ b/ui/views/views.go @@ -132,16 +132,28 @@ func FormatAge(age uint32) string { return fmt.Sprintf("%02dy%02dm%02dd", age/52596, (age%52596)/4383, (age%4383)/144) } +func interp(a, b [3]float64, r float64) (result [3]float64) { + result[0] = a[0] + (b[0]-a[0])*r + result[1] = a[1] + (b[1]-a[1])*r + result[2] = a[2] + (b[2]-a[2])*r + return +} + func ColorizeAge(age uint32, text string, opts ...lntcolor.Option) string { - s := 1.0 - l := 0.5 - if age < 52596 { - s = float64(age) / 52596 + ageColors := [][3]float64{ + {120, 0.9, 0.9}, + {60, 0.9, 0.6}, + {22, 1, 0.5}, } - if age < 8766 { // increase brightness slightly for 16 color terminals or else it renders as black - l = 0.66 + cur := [3]float64{} + if age < 26298 { + cur = interp(ageColors[0], ageColors[1], float64(age)/26298) + } else if age < 52596 { + cur = interp(ageColors[1], ageColors[2], float64(age-26298)/26298) + } else { + cur = ageColors[2] } - val := color.HSL(22./360, s, l).C256().Value() + val := color.HSL(cur[0]/360, cur[1], cur[2]).C256().Value() c := color.S256(val) options := lntcolor.NewOptions(opts) if options.Bold { From a178a82fef100290936ab497cde9a3f716aa99cd Mon Sep 17 00:00:00 2001 From: rkfg Date: Sun, 18 Sep 2022 15:47:46 +0300 Subject: [PATCH 4/7] Add color option --- README.md | 7 +++++++ config/config.go | 13 ++++++++++++- config/default.go | 7 +++++++ ui/views/channels.go | 7 ++++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2666935..b2280de 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,13 @@ columns = [ # "NUPD", # number of channel updates ] +[views.channels.options] +# Currently only one option for the AGE column. If enabled, uses multiple colors +# from green to orange to indicate the channel age using 256 color scheme in +# supported terminals + +# AGE = { color = "color" } + [views.transactions] # It is possible to add, remove and order columns of the # table with the array columns. The available values are: diff --git a/config/config.go b/config/config.go index f87deec..3753009 100644 --- a/config/config.go +++ b/config/config.go @@ -42,8 +42,19 @@ type Views struct { Routing *View `toml:"routing"` } +type ColumnOptions map[string]map[string]string + type View struct { - Columns []string `toml:"columns"` + Columns []string `toml:"columns"` + Options ColumnOptions `toml:"options"` +} + +func (co ColumnOptions) GetOption(columnName, option string) string { + if o, ok := co[columnName]; !ok { + return "" + } else { + return o[option] + } } type Aliases map[string]string diff --git a/config/default.go b/config/default.go index 0fafa7f..f69c89e 100644 --- a/config/default.go +++ b/config/default.go @@ -50,6 +50,13 @@ columns = [ # "NUPD", # number of channel updates ] +[views.channels.options] +# Currently only one option for the AGE column. If enabled, uses multiple colors +# from green to orange to indicate the channel age using 256 color scheme in +# supported terminals + +# AGE = { color = "color" } + [views.transactions] # It is possible to add, remove and order columns of the # table with the array columns. The available values are: diff --git a/ui/views/channels.go b/ui/views/channels.go index 0c14805..cf6fc56 100644 --- a/ui/views/channels.go +++ b/ui/views/channels.go @@ -651,7 +651,12 @@ func NewChannels(cfg *config.View, chans *models.Channels) *Channels { if c.ID == 0 { return fmt.Sprintf("%10s", "") } - return ColorizeAge(c.Age, printer.Sprintf("%10s", FormatAge(c.Age)), opts...) + result := printer.Sprintf("%10s", FormatAge(c.Age)) + if cfg.Options.GetOption("AGE", "color") == "color" { + return ColorizeAge(c.Age, result, opts...) + } else { + return color.White(opts...)(result) + } }, } From 03266a4d661f040b32b8dd82979fa9b713c30140 Mon Sep 17 00:00:00 2001 From: rkfg Date: Mon, 19 Sep 2022 11:53:59 +0300 Subject: [PATCH 5/7] Don't set channel age if ID isn't set --- ui/models/models.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/models/models.go b/ui/models/models.go index 9ada4fc..910966e 100644 --- a/ui/models/models.go +++ b/ui/models/models.go @@ -62,7 +62,9 @@ func (m *Models) RefreshChannels(ctx context.Context) error { if channel != nil && (channel.UpdatesCount < channels[i].UpdatesCount || channel.LastUpdate == nil || channel.LocalPolicy == nil || channel.RemotePolicy == nil) { - channels[i].Age = m.Info.BlockHeight - uint32(channels[i].ID>>40) + if channels[i].ID > 0 { + channels[i].Age = m.Info.BlockHeight - uint32(channels[i].ID>>40) + } err := m.network.GetChannelInfo(ctx, channels[i]) if err != nil { return err From 8c172646b63ed98807ac18f59cbb7144efc99bce Mon Sep 17 00:00:00 2001 From: rkfg Date: Mon, 19 Sep 2022 12:38:35 +0300 Subject: [PATCH 6/7] Refactor 256 color --- ui/color/color.go | 61 +++++++++++++++++++++++++++++++---------------- ui/views/views.go | 13 +++------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/ui/color/color.go b/ui/color/color.go index b1b53d3..7920c9e 100644 --- a/ui/color/color.go +++ b/ui/color/color.go @@ -31,11 +31,11 @@ func SprintFunc(c color.Style) func(args ...interface{}) string { type Option func(*options) type options struct { - Bold bool - Bg bool + bold bool + bg bool } -func NewOptions(opts []Option) options { +func newOptions(opts []Option) options { options := options{} for i := range opts { if opts[i] == nil { @@ -46,24 +46,24 @@ func NewOptions(opts []Option) options { return options } -func Bold(o *options) { o.Bold = true } -func Background(o *options) { o.Bg = true } +func Bold(o *options) { o.bold = true } +func Background(o *options) { o.bg = true } func Yellow(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bold { + options := newOptions(opts) + if options.bold { return yellowBold } return yellow } func Green(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bold { + options := newOptions(opts) + if options.bold { return greenBold } - if options.Bg { + if options.bg { return greenBg } @@ -71,44 +71,63 @@ func Green(opts ...Option) func(a ...interface{}) string { } func Red(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bold { + options := newOptions(opts) + if options.bold { return redBold } return red } func White(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bold { + options := newOptions(opts) + if options.bold { return whiteBold } return white } func Cyan(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bold { + options := newOptions(opts) + if options.bold { return cyanBold } - if options.Bg { + if options.bg { return cyanBg } return cyan } func Black(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bg { + options := newOptions(opts) + if options.bg { return blackBg } return black } func Magenta(opts ...Option) func(a ...interface{}) string { - options := NewOptions(opts) - if options.Bg { + options := newOptions(opts) + if options.bg { return magentaBg } return magentaBg } + +func HSL256(h, s, l float64, opts ...Option) func(a ...interface{}) string { + options := newOptions(opts) + val := color.HSL(h, s, l).C256().Value() + c := color.S256(val) + if options.bg { + fg := color.White.C256().Value() + if l > 0.5 { + fg = color.Black.C256().Value() + } + c = color.S256(fg, val) + } + if options.bold { + c.AddOpts(color.Bold) + } + return func(a ...interface{}) string { + return c.Sprint(a...) + } +} diff --git a/ui/views/views.go b/ui/views/views.go index bfde012..cccf67e 100644 --- a/ui/views/views.go +++ b/ui/views/views.go @@ -7,10 +7,9 @@ import ( "github.com/pkg/errors" "github.com/edouardparis/lntop/config" - lntcolor "github.com/edouardparis/lntop/ui/color" + "github.com/edouardparis/lntop/ui/color" "github.com/edouardparis/lntop/ui/cursor" "github.com/edouardparis/lntop/ui/models" - "github.com/gookit/color" ) type View interface { @@ -139,7 +138,7 @@ func interp(a, b [3]float64, r float64) (result [3]float64) { return } -func ColorizeAge(age uint32, text string, opts ...lntcolor.Option) string { +func ColorizeAge(age uint32, text string, opts ...color.Option) string { ageColors := [][3]float64{ {120, 0.9, 0.9}, {60, 0.9, 0.6}, @@ -153,11 +152,5 @@ func ColorizeAge(age uint32, text string, opts ...lntcolor.Option) string { } else { cur = ageColors[2] } - val := color.HSL(cur[0]/360, cur[1], cur[2]).C256().Value() - c := color.S256(val) - options := lntcolor.NewOptions(opts) - if options.Bold { - c.AddOpts(color.Bold) - } - return c.Sprint(text) + return color.HSL256(cur[0]/360, cur[1], cur[2], opts...)(text) } From 48b7b58eeea46917a111ad70085a3443ae9512af Mon Sep 17 00:00:00 2001 From: rkfg Date: Fri, 23 Sep 2022 11:16:45 +0300 Subject: [PATCH 7/7] Fix channel age update --- ui/models/channels.go | 1 + ui/models/models.go | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/models/channels.go b/ui/models/channels.go index 93dc9d4..b26ad5c 100644 --- a/ui/models/channels.go +++ b/ui/models/channels.go @@ -104,6 +104,7 @@ func (c *Channels) Update(newChannel *models.Channel) { oldChannel.CSVDelay = newChannel.CSVDelay oldChannel.Private = newChannel.Private oldChannel.PendingHTLC = newChannel.PendingHTLC + oldChannel.Age = newChannel.Age if newChannel.LastUpdate != nil { oldChannel.LastUpdate = newChannel.LastUpdate diff --git a/ui/models/models.go b/ui/models/models.go index 910966e..e606f73 100644 --- a/ui/models/models.go +++ b/ui/models/models.go @@ -55,6 +55,9 @@ func (m *Models) RefreshChannels(ctx context.Context) error { index := map[string]*models.Channel{} for i := range channels { index[channels[i].ChannelPoint] = channels[i] + if channels[i].ID > 0 { + channels[i].Age = m.Info.BlockHeight - uint32(channels[i].ID>>40) + } if !m.Channels.Contains(channels[i]) { m.Channels.Add(channels[i]) } @@ -62,9 +65,6 @@ func (m *Models) RefreshChannels(ctx context.Context) error { if channel != nil && (channel.UpdatesCount < channels[i].UpdatesCount || channel.LastUpdate == nil || channel.LocalPolicy == nil || channel.RemotePolicy == nil) { - if channels[i].ID > 0 { - channels[i].Age = m.Info.BlockHeight - uint32(channels[i].ID>>40) - } err := m.network.GetChannelInfo(ctx, channels[i]) if err != nil { return err