Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module github.com/nakabonne/ali | ||
|
||
go 1.15 | ||
|
||
require ( | ||
github.com/mum4k/termdash v0.12.2 | ||
github.com/spf13/pflag v1.0.5 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= | ||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= | ||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= | ||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= | ||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= | ||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= | ||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= | ||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= | ||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||
github.com/mum4k/termdash v0.12.2 h1:S2frz71OrXUKIVVZ3snYBEzyYlUNRTu0ElV6d5Pf6gI= | ||
github.com/mum4k/termdash v0.12.2/go.mod h1:haerPCSO0U8pehROAecmuOHDF+2UXw2KaCTxdWooDFE= | ||
github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be h1:yzmWtPyxEUIKdZg4RcPq64MfS8NA6A5fNOJgYhpR9EQ= | ||
github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package gui | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/mum4k/termdash" | ||
"github.com/mum4k/termdash/container" | ||
"github.com/mum4k/termdash/container/grid" | ||
"github.com/mum4k/termdash/keyboard" | ||
"github.com/mum4k/termdash/linestyle" | ||
"github.com/mum4k/termdash/terminal/termbox" | ||
"github.com/mum4k/termdash/terminal/terminalapi" | ||
) | ||
|
||
const rootID = "root" | ||
|
||
func Run() error { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
|
||
t, err := termbox.New(termbox.ColorMode(terminalapi.ColorMode256)) | ||
if err != nil { | ||
return fmt.Errorf("failed to generate terminal interface: %w", err) | ||
} | ||
defer t.Close() | ||
|
||
c, err := container.New(t, container.ID(rootID)) | ||
if err != nil { | ||
return fmt.Errorf("failed to generate container: %w", err) | ||
} | ||
|
||
w, err := newWidgets(ctx, c) | ||
if err != nil { | ||
return fmt.Errorf("failed to generate widgets: %w", err) | ||
} | ||
gridOpts, err := gridLayout(w) | ||
if err != nil { | ||
return fmt.Errorf("failed to build grid layout: %w", err) | ||
} | ||
if err := c.Update(rootID, gridOpts...); err != nil { | ||
return fmt.Errorf("failed to update container: %w", err) | ||
} | ||
|
||
quitter := func(k *terminalapi.Keyboard) { | ||
if k.Key == keyboard.KeyEsc || k.Key == keyboard.KeyCtrlC { | ||
cancel() | ||
} | ||
} | ||
return termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(redrawInterval)) | ||
} | ||
|
||
func gridLayout(w *widgets) ([]container.Option, error) { | ||
rows := []grid.Element{ | ||
grid.RowHeightPerc(99, | ||
grid.Widget(w.plotChart, | ||
container.Border(linestyle.Light), | ||
container.BorderTitle("Plot"), | ||
), | ||
), | ||
} | ||
col := grid.ColWidthPerc(99, rows...) | ||
|
||
builder := grid.New() | ||
builder.Add( | ||
col, | ||
) | ||
|
||
return builder.Build() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package gui | ||
|
||
import ( | ||
"context" | ||
"math" | ||
"time" | ||
|
||
"github.com/mum4k/termdash/cell" | ||
"github.com/mum4k/termdash/container" | ||
"github.com/mum4k/termdash/widgets/button" | ||
"github.com/mum4k/termdash/widgets/linechart" | ||
"github.com/mum4k/termdash/widgets/textinput" | ||
) | ||
|
||
// redrawInterval is how often termdash redraws the screen. | ||
const ( | ||
redrawInterval = 250 * time.Millisecond | ||
) | ||
|
||
type widgets struct { | ||
URLInput *textinput.TextInput | ||
attackButton *button.Button | ||
plotChart *linechart.LineChart | ||
} | ||
|
||
func newWidgets(ctx context.Context, c *container.Container) (*widgets, error) { | ||
l, err := newLineChart(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &widgets{ | ||
URLInput: nil, | ||
attackButton: nil, | ||
plotChart: l, | ||
}, nil | ||
} | ||
|
||
// newLineChart returns a line plotChart that displays a heartbeat-like progression. | ||
func newLineChart(ctx context.Context) (*linechart.LineChart, error) { | ||
var inputs []float64 | ||
for i := 0; i < 100; i++ { | ||
v := math.Pow(math.Sin(float64(i)), 63) * math.Sin(float64(i)+1.5) * 8 | ||
inputs = append(inputs, v) | ||
} | ||
|
||
lc, err := linechart.New( | ||
linechart.AxesCellOpts(cell.FgColor(cell.ColorRed)), | ||
linechart.YLabelCellOpts(cell.FgColor(cell.ColorGreen)), | ||
linechart.XLabelCellOpts(cell.FgColor(cell.ColorGreen)), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
step := 0 | ||
go periodic(ctx, redrawInterval/3, func() error { | ||
step = (step + 1) % len(inputs) | ||
return lc.Series("heartbeat", rotateFloats(inputs, step), | ||
linechart.SeriesCellOpts(cell.FgColor(cell.ColorNumber(87))), | ||
linechart.SeriesXLabels(map[int]string{ | ||
0: "zero", | ||
}), | ||
) | ||
}) | ||
return lc, nil | ||
} | ||
|
||
// periodic executes the provided closure periodically every interval. | ||
// Exits when the context expires. | ||
func periodic(ctx context.Context, interval time.Duration, fn func() error) { | ||
ticker := time.NewTicker(interval) | ||
defer ticker.Stop() | ||
for { | ||
select { | ||
case <-ticker.C: | ||
if err := fn(); err != nil { | ||
panic(err) | ||
} | ||
case <-ctx.Done(): | ||
return | ||
} | ||
} | ||
} | ||
|
||
// rotateFloats returns a new slice with inputs rotated by step. | ||
// I.e. for a step of one: | ||
// inputs[0] -> inputs[len(inputs)-1] | ||
// inputs[1] -> inputs[0] | ||
// And so on. | ||
func rotateFloats(inputs []float64, step int) []float64 { | ||
return append(inputs[step:], inputs[:step]...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"os" | ||
"runtime" | ||
|
||
flag "github.com/spf13/pflag" | ||
|
||
"github.com/nakabonne/ali/gui" | ||
) | ||
|
||
var ( | ||
flagSet = flag.NewFlagSet("ali", flag.ContinueOnError) | ||
|
||
usage = func() { | ||
fmt.Fprintln(os.Stderr, "usage: ali [<flag> ...]") | ||
flagSet.PrintDefaults() | ||
} | ||
// Automatically populated by goreleaser during build | ||
version = "unversioned" | ||
commit = "?" | ||
date = "?" | ||
) | ||
|
||
type cli struct { | ||
debug bool | ||
version bool | ||
stdout io.Writer | ||
stderr io.Writer | ||
} | ||
|
||
func main() { | ||
c := &cli{ | ||
stdout: os.Stdout, | ||
stderr: os.Stderr, | ||
} | ||
flagSet.BoolVarP(&c.version, "version", "v", false, "print the current version") | ||
flagSet.BoolVar(&c.debug, "debug", false, "run in debug mode") | ||
flagSet.Usage = usage | ||
if err := flagSet.Parse(os.Args[1:]); err != nil { | ||
if !errors.Is(err, flag.ErrHelp) { | ||
fmt.Fprintln(c.stderr, err) | ||
} | ||
return | ||
} | ||
|
||
os.Exit(c.run()) | ||
} | ||
func (c *cli) run() int { | ||
if c.version { | ||
fmt.Fprintf(c.stderr, "version=%s, commit=%s, buildDate=%s, os=%s, arch=%s\n", version, commit, date, runtime.GOOS, runtime.GOARCH) | ||
return 0 | ||
} | ||
|
||
if err := gui.Run(); err != nil { | ||
fmt.Fprintf(c.stderr, "failed to start application: %s", err.Error()) | ||
return 1 | ||
} | ||
|
||
return 0 | ||
} |