//doLiveGraph builds a graph in the terminal window that //updates every graphUpdate seconds //It will build up to maxGraphs graphs with one time-series //per graph func doLiveGraph(res *wavefront.QueryResponse, query *wavefront.Querying, period int64) { err := ui.Init() if err != nil { log.Fatal(err) } defer ui.Close() if maxGraphs > len(res.TimeSeries) { maxGraphs = len(res.TimeSeries) } var wDivisor, hDivisor int switch maxGraphs { case 1: wDivisor = 1 hDivisor = 1 case 2: wDivisor = 2 hDivisor = 1 case 3, 4: wDivisor = 2 hDivisor = 2 } height := ui.TermHeight() / hDivisor width := ui.TermWidth() / wDivisor xVals, yVals := calculateCoords(maxGraphs, ui.TermWidth()/wDivisor, ui.TermHeight()/hDivisor) graphs := buildGraphs(res, height, width, xVals, yVals) ui.Render(graphs...) ui.Handle("/sys/kbd/q", func(ui.Event) { // press q to quit ui.StopLoop() }) ui.Handle("/sys/kbd/C-c", func(ui.Event) { // handle Ctrl + c combination ui.StopLoop() }) ui.Handle("/timer/1s", func(e ui.Event) { query.SetEndTime(time.Now()) query.SetStartTime(period) res, err := query.Execute() if err != nil { log.Fatal(err) } graphs := buildGraphs(res, height, width, xVals, yVals) ui.Render(graphs...) }) ui.Loop() }
func readMessage(message *imap.MessageInfo) { set := new(imap.SeqSet) set.AddNum(message.Seq) cmd, err := imap.Wait(c.Fetch(set, BODY_PART_NAME)) panicMaybe(err) reader, err := messageReader(cmd.Data[0].MessageInfo()) panicMaybe(err) scanner := bufio.NewScanner(reader) var lines []string for scanner.Scan() { lines = append(lines, scanner.Text()) } messageBodyStr := strings.Join(lines[:min(len(lines), ui.TermHeight()-2)], "\n") if len(messageBodyStr) <= 0 { LOG.Printf("Message body was empty or could not be retrieved: +%v\n", err) return } msgBox := ui.NewPar(messageBodyStr) msgBox.Border.Label = "Reading Message" msgBox.Height = ui.TermHeight() msgBox.Width = ui.TermWidth() msgBox.Y = 0 ui.Render(msgBox) topLineIndex := 0 redraw := make(chan bool) for { select { case e := <-ui.EventCh(): switch e.Key { case ui.KeyArrowDown: topLineIndex = max(0, min( len(lines)-msgBox.Height/2, topLineIndex+1)) go func() { redraw <- true }() case ui.KeyArrowUp: topLineIndex = max(0, topLineIndex-1) go func() { redraw <- true }() case ui.KeyEsc: // back to "list messages" return } case <-redraw: messageBodyStr = strings.Join(lines[topLineIndex+1:], "\n") msgBox.Text = messageBodyStr ui.Render(msgBox) } } }
func (p *LabelListPage) Create() { ui.Clear() ls := ui.NewList() p.uiList = ls if p.statusBar == nil { p.statusBar = new(StatusBar) } if p.commandBar == nil { p.commandBar = commandBar } queryName := p.ActiveQuery.Name queryJQL := p.ActiveQuery.JQL p.labelCounts = countLabelsFromQuery(queryJQL) p.cachedResults = p.labelsAsSortedList() p.isPopulated = true p.displayLines = make([]string, len(p.cachedResults)) ls.ItemFgColor = ui.ColorYellow ls.BorderLabel = fmt.Sprintf("Label view -- %s: %s", queryName, queryJQL) ls.Height = ui.TermHeight() - 2 ls.Width = ui.TermWidth() ls.Y = 0 p.statusBar.Create() p.commandBar.Create() p.Update() }
func BuildUI() { ui.Init() defer ui.Close() receiveBox := CreateReceiveBox() sendBox := CreateSendBox() ui.Body.AddRows( ui.NewRow(ui.NewCol(12, 0, receiveBox)), ui.NewRow(ui.NewCol(12, 0, sendBox)), ) ui.Body.Align() ui.Render(ui.Body) ui.Handle("/sys/kbd/C-x", func(e ui.Event) { ui.StopLoop() }) ui.Handle("/timer/1s", func(e ui.Event) { ReceiveBoxHeight = ui.TermHeight() - SendBoxHeight receiveBox.Height = ReceiveBoxHeight ui.Body.Align() ui.Render(ui.Body) }) // Leaving this commented out for now // I'd like to get this method of screen refreshing working instead of the 1s method, // but this crashes on resize. // ui.Handle("/sys/wnd/resize", func(e ui.Event) { // ui.Body.Align() // ui.Render(ui.Body) // }) ui.Loop() }
func (p *TicketListPage) Create() { log.Debugf("TicketListPage.Create(): self: %s (%p)", p.Id(), p) log.Debugf("TicketListPage.Create(): currentPage: %s (%p)", currentPage.Id(), currentPage) ui.Clear() ls := ui.NewList() p.uiList = ls if p.statusBar == nil { p.statusBar = new(StatusBar) } if p.commandBar == nil { p.commandBar = commandBar } query := p.ActiveQuery.JQL if sort := p.ActiveSort.JQL; sort != "" { re := regexp.MustCompile(`(?i)\s+ORDER\s+BY.+$`) query = re.ReplaceAllString(query, ``) + " " + sort } if len(p.cachedResults) == 0 { p.cachedResults = JiraQueryAsStrings(query, p.ActiveQuery.Template) } if p.selectedLine >= len(p.cachedResults) { p.selectedLine = len(p.cachedResults) - 1 } p.displayLines = make([]string, len(p.cachedResults)) ls.ItemFgColor = ui.ColorYellow ls.BorderLabel = fmt.Sprintf("%s: %s", p.ActiveQuery.Name, p.ActiveQuery.JQL) ls.Height = ui.TermHeight() - 2 ls.Width = ui.TermWidth() ls.Y = 0 p.statusBar.Create() p.commandBar.Create() p.Update() }
func (ed *Editor) resize() { termui.Body.Width = termui.TermWidth() ed.applets.Height = termui.TermHeight() - 2 + 1 // offset 1 for border ed.desc.Y = ed.locked.Y + ed.locked.Height - 1 // offset 1 for border ed.desc.Height = termui.TermHeight() - ed.desc.Y - 2 ed.title.Y = termui.TermHeight() - 2 ed.title.Width = termui.TermWidth() + 1 // offset 1 for border ed.appinfo.Width = termui.TermWidth() - ed.appinfo.X + 1 // offset 1 for border ed.locked.Width = termui.TermWidth() - ed.applets.Width + 1 // offset 1 for border ed.desc.Width = termui.TermWidth() - ed.applets.Width + 1 // offset 1 for border ed.desc.WrapLength = ed.desc.Width termui.Render(ed.applets, ed.fields, ed.appinfo, ed.locked, ed.desc, ed.title) }
func (ui *tatui) initHomeLeft() { textURL := viper.GetString("url") if textURL == "" { textURL = "[Invalid URL, please check your config file](fg-red)" } p := termui.NewPar(` TEXT AND TAGS ---------------------------------------------- ---------------------------------------------- ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| ||| Tatcli Version: ` + internal.VERSION + ` https://github.com/ovh/tatcli TAT Engine: https://github.com/ovh/tat Current Tat Engine: ` + textURL + ` Current config file: ` + internal.ConfigFile + ` Shortcuts: - Ctrl + a to view all topics. Cmd /topics in send box - Ctrl + b to go back to messsages list, after selected a message - Ctrl + c clears filters and UI on current messages list - Ctrl + f to view favorites topics. Cmd /favorites - Ctrl + h to go back home. Cmd /home or /help - Ctrl + t hide or show top menu. Cmd /toggle-top - Ctrl + y hide or show actionbox menu. Cmd /toggle-bottom - Ctrl + o open current message on tatwebui with a browser. Cmd /open Use option tatwebui-url in config file. See /set-tatwebui-url - Ctrl + p open links in current message with a browser. Cmd /open-links - Ctrl + j / Ctrl + k (for reverse action): if mode run is enabled, set a msg from open to doing, from doing to done from done to open. if mode monitoring is enabled, set a msg from UP to AL, from AL to UP. - Ctrl + q to quit. Cmd /quit - Ctrl + r to view unread topics. Cmd /unread - Ctrl + u display/hide usernames in messages list. Cmd /toggle-usernames - UP / Down to move into topics & messages list - UP / Down to navigate through history of action box - <tab> to go to next section on screen`) p.Height = termui.TermHeight() - uiHeightTop - uiHeightSend p.TextFgColor = termui.ColorWhite p.BorderTop = true p.BorderLeft = false p.BorderBottom = false ui.homeLeft = p }
func (p *CommandBar) Create() { ls := ui.NewList() p.uiList = ls ls.ItemFgColor = ui.ColorGreen ls.Border = false ls.Height = 1 ls.Width = ui.TermWidth() ls.X = 0 ls.Y = ui.TermHeight() - 1 p.Update() }
func adjustDimensions() { termui.Body.Width = termui.TermWidth() height := termui.TermHeight() parMap["moveHistory"].Height = height - 23 - 4 parMap["output"].Height = height - 23 if height < 31 { parMap["board"].Height = height - 8 parMap["moveHistory"].Height = 2 parMap["output"].Height = 6 } }
func (p *TicketShowPage) Create() { log.Debugf("TicketShowPage.Create(): self: %s (%p)", p.Id(), p) log.Debugf("TicketShowPage.Create(): currentPage: %s (%p)", currentPage.Id(), currentPage) p.opts = getJiraOpts() if p.TicketId == "" { p.TicketId = ticketListPage.GetSelectedTicketId() } if p.MaxWrapWidth == 0 { if m := p.opts["max_wrap"]; m != nil { p.MaxWrapWidth = uint(m.(int64)) } else { p.MaxWrapWidth = defaultMaxWrapWidth } } ui.Clear() ls := ui.NewList() if p.statusBar == nil { p.statusBar = new(StatusBar) } if p.commandBar == nil { p.commandBar = commandBar } p.uiList = ls if p.Template == "" { if templateOpt := p.opts["template"]; templateOpt == nil { p.Template = "jira_ui_view" } else { p.Template = templateOpt.(string) } } innerWidth := uint(ui.TermWidth()) - 3 if innerWidth < p.MaxWrapWidth { p.WrapWidth = innerWidth } else { p.WrapWidth = p.MaxWrapWidth } if p.apiBody == nil { p.apiBody, _ = FetchJiraTicket(p.TicketId) } p.cachedResults = WrapText(JiraTicketAsStrings(p.apiBody, p.Template), p.WrapWidth) p.displayLines = make([]string, len(p.cachedResults)) if p.selectedLine >= len(p.cachedResults) { p.selectedLine = len(p.cachedResults) - 1 } ls.ItemFgColor = ui.ColorYellow ls.Height = ui.TermHeight() - 2 ls.Width = ui.TermWidth() ls.Border = true ls.BorderLabel = fmt.Sprintf("%s %s", p.TicketId, p.ticketTrailAsString()) ls.Y = 0 p.statusBar.Create() p.commandBar.Create() p.Update() }
func (p *StatusBar) Create() { ls := ui.NewList() p.uiList = ls ls.ItemFgColor = ui.ColorWhite ls.ItemBgColor = ui.ColorRed ls.Bg = ui.ColorRed ls.Border = false ls.Height = 1 ls.Width = ui.TermWidth() ls.X = 0 ls.Y = ui.TermHeight() - 2 p.Update() }
func (p *BaseListPage) Create() { ui.Clear() ls := ui.NewList() p.uiList = ls p.cachedResults = make([]string, 0) p.displayLines = make([]string, len(p.cachedResults)) ls.ItemFgColor = ui.ColorYellow ls.BorderLabel = "Updating, please wait" ls.Height = ui.TermHeight() ls.Width = ui.TermWidth() ls.Y = 0 p.Update() }
func (ui *tatui) initHomeRight() { p := termui.NewPar(`Action Box Keywords: - /help display this page - /me show information about you - /version to show tatcli and engine version On messages list: - /label eeeeee yourLabel to add a label on selected message - /unlabel yourLabel to remove label "yourLabel" on selected message - /voteup, /votedown, /unvoteup, /unvotedown to vote up or down, or remove vote - /task, /untask to add or remove selected message as a personal task - /like, /unlike to add or remove like on selected message - /filter label:labelA,labelB andtag:tag,tagb - /mode (run|monitoring): enable Ctrl + l shortcut, see on left side for help - /codereview splits screen into fours panes: label:OPENED label:APPROVED label:MERGED label:DECLINED - /monitoring splits screen into three panes: label:UP, label:AL, notlabel:AL,UP This is the same as two commands: - /split label:UP label:AL notlabel:AL,UP - /mode monitoring - /run <tag> splits screen into three panes: label:open, label:doing, label:done /run AA,BB is the same as two commands: - /split tag:AA,BB;label:open tag:AA,BB;label:doing tag:AA,BB;label:done - /mode run - /set-tatwebui-url <urlOfTatWebUI> sets tatwebui-url in tatcli config file. This url is used by Ctrl + o shortcut to open message with a tatwebui instance. - /split <criteria> splits screen with one section per criteria delimited by space, ex: /split label:labelA label:labelB label:labelC /split label:labelA,labelB andtag:tag,tagb /split tag:myTag;label:labelA,labelB andtag:tag,tagb;label:labelC - /save saves current filters in tatcli config file - /toggle-usernames displays or hides username in messages list For /split and /filter, see all parameters on https://github.com/ovh/tat#parameters On topics list, ex: - /filter topic:/Private/firstname.lastname see all parameters on https://github.com/ovh/tat#parameters-4 `) p.Height = termui.TermHeight() - uiHeightTop - uiHeightSend p.TextFgColor = termui.ColorWhite p.BorderTop = true p.BorderLeft = false p.BorderRight = false p.BorderBottom = false ui.homeRight = p }
func (p *BaseInputBox) Create() { ls := ui.NewList() var strs []string p.uiList = ls ls.Items = strs ls.ItemFgColor = ui.ColorGreen ls.BorderFg = ui.ColorRed ls.Height = 1 ls.Width = 30 ls.Overflow = "wrap" ls.X = ui.TermWidth()/2 - ls.Width/2 ls.Y = ui.TermHeight()/2 - ls.Height/2 p.Update() }
func (p *PasswordInputBox) Create() { ls := ui.NewList() p.uiList = ls var strs []string ls.Items = strs ls.ItemFgColor = ui.ColorGreen ls.BorderLabel = "Enter Password:" ls.BorderFg = ui.ColorRed ls.Height = 3 ls.Width = 30 ls.X = ui.TermWidth()/2 - ls.Width/2 ls.Y = ui.TermHeight()/2 - ls.Height/2 p.Update() }
func refresh() { prompt.Text = promptMsg nreport := ui.TermHeight() - 13 ls.Height = nreport if len(reportItems) > nreport*reportPage { // can seek to the page ls.Items = reportItems[nreport*reportPage : len(reportItems)] } else { ls.Items = []string{} } ui.Body.Align() ui.Render(ui.Body) }
func setupBody() { height := termui.TermHeight() - 23 prompt := termui.NewPar("") prompt.Height = 1 prompt.Border = false parMap["prompt"] = prompt input := termui.NewPar("") input.Height = 3 input.BorderLabel = "Input" input.BorderFg = termui.ColorYellow parMap["input"] = input moveHistory := termui.NewPar("") moveHistory.Height = height - 4 moveHistory.BorderLabel = "Move History" moveHistory.BorderFg = termui.ColorBlue parMap["moveHistory"] = moveHistory linesMap["moveHistory"] = NewLines() output := termui.NewPar("") output.Height = height output.BorderLabel = "Output" output.BorderFg = termui.ColorGreen parMap["output"] = output linesMap["output"] = NewLines() board := termui.NewPar("") board.Height = 23 board.Width = 37 board.BorderLabel = "Board" board.BorderFg = termui.ColorRed parMap["board"] = board // build layout termui.Body.AddRows( termui.NewRow( termui.NewCol(6, 0, parMap["prompt"], parMap["input"], parMap["moveHistory"]), termui.NewCol(6, 0, parMap["output"]), ), termui.NewRow( termui.NewCol(12, 0, parMap["board"]), ), ) changeState(0) }
func showSubreddit(subredditName string) error { r := geddit.NewSession("r by /u/bnadland") submissions, err := r.SubredditSubmissions(subredditName, geddit.HotSubmissions, geddit.ListingOptions{}) if err != nil { return err } isActive := true cursor := 3 for isActive { entries := []string{} for i, submission := range submissions { entries = append(entries, fmt.Sprintf("%s %s", isActiveCursor(cursor, i), submission.Title)) } ls := termui.NewList() ls.Items = entries ls.ItemFgColor = termui.ColorDefault ls.Border.Label = fmt.Sprintf("Subreddit: %s", subredditName) ls.Height = termui.TermHeight() ls.Width = termui.TermWidth() ls.Y = 0 termui.Render(ls) event := <-termui.EventCh() if event.Type == termui.EventKey { switch event.Key { case termui.KeyArrowLeft: isActive = false case termui.KeyArrowDown: cursor = cursor + 1 if cursor > len(submissions) { cursor = len(submissions) } case termui.KeyArrowUp: cursor = cursor - 1 if cursor < 0 { cursor = 0 } } } } return nil }
func main() { if len(os.Args) < 2 { log.Fatal("Usage: ", os.Args[0], " <sparkyfish server hostname/IP>[:port]") } dest := os.Args[1] i := last(dest, ':') if i < 0 { dest = fmt.Sprint(dest, ":7121") } // Initialize our screen err := termui.Init() if err != nil { panic(err) } if termui.TermWidth() < 60 || termui.TermHeight() < 28 { fmt.Println("sparkyfish needs a terminal window at least 60x28 to run.") os.Exit(1) } defer termui.Close() // 'q' quits the program termui.Handle("/sys/kbd/q", func(termui.Event) { termui.StopLoop() }) // 'Q' also works termui.Handle("/sys/kbd/Q", func(termui.Event) { termui.StopLoop() }) sc := newsparkyClient() sc.serverHostname = dest sc.prepareChannels() sc.wr = newwidgetRenderer() // Begin our tests go sc.runTestSequence() termui.Loop() }
func (ui *tatui) initMessage() { strs := []string{"[Loading...](fg-black,bg-white)"} ls := termui.NewList() ls.BorderTop, ls.BorderLeft, ls.BorderRight, ls.BorderBottom = true, false, false, false ls.Items = strs ls.ItemFgColor = termui.ColorWhite ls.BorderLabel = "Message" ls.Overflow = "wrap" ls.Width = 25 ls.Y = 0 ls.Height = termui.TermHeight() - uiHeightTop - uiHeightSend _, ok := ui.uilists[uiMessages][ui.selectedPaneMessages] if ok && ui.uilists[uiMessages][ui.selectedPaneMessages].position >= 0 && ui.uilists[uiMessages][ui.selectedPaneMessages].position < len(ui.currentListMessages[ui.selectedPaneMessages]) { ui.currentMessage = ui.currentListMessages[ui.selectedPaneMessages][ui.uilists[uiMessages][ui.selectedPaneMessages].position] ui.uilists[uiMessage][0] = &uilist{uiType: uiMessage, list: ls, position: 0, page: 0, update: ui.updateMessage} } }
func (p *SortOrderPage) Create() { ls := ui.NewList() p.uiList = ls p.selectedLine = 0 p.firstDisplayLine = 0 if len(p.cachedResults) == 0 { p.cachedResults = getSorts() p.displayLines = make([]string, len(p.cachedResults)) } ls.ItemFgColor = ui.ColorGreen ls.BorderLabel = "Sort By..." ls.BorderFg = ui.ColorRed ls.Height = 10 ls.Width = 50 ls.X = ui.TermWidth()/2 - ls.Width/2 ls.Y = ui.TermHeight()/2 - ls.Height/2 p.Update() }
func (ui *tatui) initMessages() { for k := range ui.currentFilterMessages[ui.currentTopic.Topic] { strs := []string{"[Loading...](fg-black,bg-white)"} ls := termui.NewList() ls.BorderTop, ls.BorderLeft, ls.BorderRight, ls.BorderBottom = true, false, false, false ls.Items = strs ls.ItemFgColor = termui.ColorWhite ls.BorderLabel = "Messages" ls.Width = 25 ls.Y = 0 ls.Height = (termui.TermHeight() - uiHeightTop - uiHeightSend) / len(ui.currentFilterMessages[ui.currentTopic.Topic]) if _, ok := ui.uilists[uiMessages]; !ok { ui.uilists[uiMessages] = make(map[int]*uilist) } ui.uilists[uiMessages][k] = &uilist{uiType: uiMessages, list: ls, position: 0, page: 0, update: ui.updateMessages} } ui.applyLabelOnMsgPanes() ui.render() }
func (ui *tatui) showResult(cmdResult, result string) { ui.current = uiResult ui.send.BorderLabel = " ✎ Action" termui.Body.Rows = nil p := termui.NewPar(`Result of ` + cmdResult + `: ` + result + ` `) p.Height = termui.TermHeight() - uiHeightTop - uiHeightSend p.TextFgColor = termui.ColorWhite p.BorderTop = true ui.prepareTopMenu() termui.Body.AddRows( termui.NewRow( termui.NewCol(12, 0, p), ), ) ui.prepareSendRow() termui.Clear() }
func (ui *tatui) initTopics() { strs := []string{"[Loading...](fg-black,bg-white)"} ls := termui.NewList() ls.BorderTop, ls.BorderLeft, ls.BorderRight, ls.BorderBottom = true, false, false, false ls.Items = strs ls.ItemFgColor = termui.ColorWhite if ui.onlyFavorites == "true" { ls.BorderLabel = " ★ Favorites Topics ★ " } else if ui.onlyUnread { ls.BorderLabel = " ✉ Unread Topics ✉ " } else { ls.BorderLabel = " All Topics " } ls.Width = 25 ls.Y = 0 ls.Height = termui.TermHeight() - uiHeightTop - uiHeightSend m := make(map[int]*uilist) m[0] = &uilist{uiType: uiTopics, list: ls, position: 0, page: 0, update: ui.updateTopics} ui.uilists[uiTopics] = m }
func (p *QueryPage) Create() { log.Debugf("QueryPage.Create(): self: %s (%p)", p.Id(), p) log.Debugf("QueryPage.Create(): currentPage: %s (%p)", currentPage.Id(), currentPage) ui.Clear() ls := ui.NewList() p.uiList = ls if p.statusBar == nil { p.statusBar = new(StatusBar) } if p.commandBar == nil { p.commandBar = commandBar } p.cachedResults = getQueries() p.displayLines = make([]string, len(p.cachedResults)) ls.ItemFgColor = ui.ColorYellow ls.BorderLabel = "Queries" ls.Height = ui.TermHeight() - 2 ls.Width = ui.TermWidth() ls.Y = 0 p.statusBar.Create() p.commandBar.Create() p.Update() }
func (p *HelpPage) Create() { ui.Clear() ls := ui.NewList() p.uiList = ls if p.statusBar == nil { p.statusBar = new(StatusBar) } if p.commandBar == nil { p.commandBar = commandBar } if len(p.cachedResults) == 0 { p.cachedResults = HelpTextAsStrings(nil, "jira_ui_help") } p.displayLines = make([]string, len(p.cachedResults)) ls.ItemFgColor = ui.ColorYellow ls.BorderLabel = "Help" ls.Height = ui.TermHeight() - 2 ls.Width = ui.TermWidth() ls.Y = 0 p.statusBar.Create() p.commandBar.Create() p.Update() }
// Relayout recalculates widgets sizes and coords. func (t *TermUISingle) Relayout() { tw, th := termui.TermWidth(), termui.TermHeight() h := th // First row: Title and Status pars firstRowH := 3 t.Title.Height = firstRowH t.Title.Width = tw / 2 if tw%2 == 1 { t.Title.Width++ } t.Status.Height = firstRowH t.Status.Width = tw / 2 t.Status.X = t.Title.X + t.Title.Width h -= firstRowH // Second row: lists secondRowH := 3 num := len(t.Pars) parW := tw / num for i, par := range t.Pars { par.Y = th - h par.Width = parW par.Height = secondRowH par.X = i * parW } if num*parW < tw { t.Pars[num-1].Width = tw - ((num - 1) * parW) } h -= secondRowH // Third row: Sparklines t.Sparkline.Width = tw t.Sparkline.Height = h t.Sparkline.Y = th - h }
// monitor starts a terminal UI based monitoring tool for the requested metrics. func monitor(ctx *cli.Context) { var ( client comms.EthereumClient err error ) // Attach to an Ethereum node over IPC or RPC endpoint := ctx.String(monitorCommandAttachFlag.Name) if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil { utils.Fatalf("Unable to attach to geth node: %v", err) } defer client.Close() xeth := rpc.NewXeth(client) // Retrieve all the available metrics and resolve the user pattens metrics, err := retrieveMetrics(xeth) if err != nil { utils.Fatalf("Failed to retrieve system metrics: %v", err) } monitored := resolveMetrics(metrics, ctx.Args()) if len(monitored) == 0 { list := expandMetrics(metrics, "") sort.Strings(list) if len(list) > 0 { utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - ")) } else { utils.Fatalf("No metrics collected by geth (--%s).\n", utils.MetricsEnabledFlag.Name) } } sort.Strings(monitored) if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 { utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - ")) } // Create and configure the chart UI defaults if err := termui.Init(); err != nil { utils.Fatalf("Unable to initialize terminal UI: %v", err) } defer termui.Close() termui.UseTheme("helloworld") rows := len(monitored) if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max { rows = max } cols := (len(monitored) + rows - 1) / rows for i := 0; i < rows; i++ { termui.Body.AddRows(termui.NewRow()) } // Create each individual data chart footer := termui.NewPar("") footer.HasBorder = true footer.Height = 3 charts := make([]*termui.LineChart, len(monitored)) units := make([]int, len(monitored)) data := make([][]float64, len(monitored)) for i := 0; i < len(monitored); i++ { charts[i] = createChart((termui.TermHeight() - footer.Height) / rows) row := termui.Body.Rows[i%rows] row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i])) } termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) refreshCharts(xeth, monitored, data, units, charts, ctx, footer) termui.Body.Align() termui.Render(termui.Body) // Watch for various system events, and periodically refresh the charts refresh := time.Tick(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second) for { select { case event := <-termui.EventCh(): if event.Type == termui.EventKey && event.Key == termui.KeyCtrlC { return } if event.Type == termui.EventResize { termui.Body.Width = termui.TermWidth() for _, chart := range charts { chart.Height = (termui.TermHeight() - footer.Height) / rows } termui.Body.Align() termui.Render(termui.Body) } case <-refresh: if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) { termui.Body.Align() } termui.Render(termui.Body) } } }
func main() { pfile := flag.String("f", "", "psafe3 file") flag.Parse() fmt.Printf("Password: "******"Last Saved: %s\nLast Saved By %s @ %s", safe.Headers.LastSave.Format("2006-01-02 15:04:05"), safe.Headers.User, safe.Headers.Host)) rightpar.Height = 2 rightpar.HasBorder = false leftpar := termui.NewPar(fmt.Sprintf("File Name: %s\nLast Program: %s", filepath.Base(*pfile), safe.Headers.ProgramSave)) leftpar.Height = 2 leftpar.HasBorder = false recordlist := termui.NewList() recordlist.Height = termui.TermHeight() - 5 recordlist.Items = getRecordList(safe) recordlist.Border.Label = fmt.Sprintf("Records (%d)", len(safe.Records)) recorddetail := termui.NewPar("") recorddetail.Height = recordlist.Height recorddetail.Border.Label = "Record Information" inputbox := termui.NewPar("") inputbox.Height = 3 inputbox.Border.Label = "Input Box ([Enter] to save, [Esc] to cancel)" inputrow := termui.NewRow(termui.NewCol(12, 0, inputbox)) commandinfo := termui.NewPar(strings.Join([]string{ "Select record by typing the index number. Edit field by typing field marker.", }, "\n")) commandinfo.Height = 3 commandinfo.Border.Label = "Help" commandrow := termui.NewRow(termui.NewCol(12, 0, commandinfo)) termui.Body.AddRows( termui.NewRow( termui.NewCol(6, 0, leftpar), termui.NewCol(6, 0, rightpar), ), termui.NewRow( termui.NewCol(6, 0, recordlist), termui.NewCol(6, 0, recorddetail), ), commandrow, ) termui.Body.Align() termui.Render(termui.Body) evt := termui.EventCh() inputMode := false valBuffer := bytes.Buffer{} numBuffer := bytes.Buffer{} var selRecord *pwsafe.Record var selField *string var inputPrompt string var startIndex int Main: for { select { case e := <-evt: if !inputMode && e.Type == termui.EventKey { switch e.Ch { case 'q': break Main case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': numBuffer.WriteRune(e.Ch) case '#': selIndex, _ := strconv.ParseInt(numBuffer.String(), 10, 64) selRecord = &safe.Records[selIndex] selField = nil numBuffer.Reset() case 'j': startIndex++ rlist := getRecordList(safe) recordlist.Items = rlist[startIndex:] case 'k': if startIndex > 1 { startIndex-- rlist := getRecordList(safe) recordlist.Items = rlist[startIndex:] } case 'a': selIndex := len(safe.Records) safe.Records = append(safe.Records, pwsafe.Record{}) selRecord = &safe.Records[selIndex] selRecord.UUID = uuid.NewV1() selRecord.CreationTime = time.Now() selField = nil rlist := getRecordList(safe) recordlist.Items = rlist[startIndex:] recordlist.Border.Label = fmt.Sprintf("Records (%d)", len(safe.Records)) case 'g': selField = &selRecord.Group inputPrompt = "Group: " inputMode = true valBuffer.WriteString(selRecord.Group) inputbox.Text = inputPrompt + valBuffer.String() case 't': selField = &selRecord.Title inputPrompt = "Title: " inputMode = true valBuffer.WriteString(selRecord.Title) inputbox.Text = inputPrompt + valBuffer.String() case 'u': selField = &selRecord.Username inputPrompt = "Username: "******"Password: "******"Url: " inputMode = true valBuffer.WriteString(selRecord.Url) inputbox.Text = inputPrompt + valBuffer.String() case 'n': selField = &selRecord.Notes inputPrompt = "Notes: " inputMode = true valBuffer.WriteString(selRecord.Notes) inputbox.Text = inputPrompt + valBuffer.String() case 'e': selField = &selRecord.Email inputPrompt = "Email: " inputMode = true valBuffer.WriteString(selRecord.Email) inputbox.Text = inputPrompt + valBuffer.String() } } else if inputMode && e.Type == termui.EventKey { if e.Key == termui.KeyEnter { if selField != nil { *selField = valBuffer.String() } valBuffer.Reset() inputMode = false inputbox.Text = "" rlist := getRecordList(safe) recordlist.Items = rlist[startIndex:] } else if e.Key == termui.KeyEsc { valBuffer.Reset() inputMode = false inputbox.Text = "" } else if e.Key == termui.KeySpace { valBuffer.WriteRune(' ') } else if e.Key == termui.KeyBackspace || e.Ch == '' { s := valBuffer.String() valBuffer = bytes.Buffer{} if len(s) > 0 { s = s[0 : len(s)-1] } valBuffer.WriteString(s) inputbox.Text = inputPrompt + valBuffer.String() } else { valBuffer.WriteRune(e.Ch) inputbox.Text = inputPrompt + valBuffer.String() } } if e.Type == termui.EventResize { termui.Body.Width = termui.TermWidth() termui.Body.Align() } if selRecord != nil { recorddetail.Text = getRecordDetail(*selRecord) } if inputMode { termui.Body.Rows[2] = inputrow } else { termui.Body.Rows[2] = commandrow } termui.Body.Align() termui.Render(termui.Body) } } oerr := pwsafe.OutputFile(*pfile, string(pw), *safe) if oerr != nil { log.Fatalln(oerr) } }
// monitor starts a terminal UI based monitoring tool for the requested metrics. func monitor(ctx *cli.Context) error { var ( client rpc.Client err error ) // Attach to an Expanse node over IPC or RPC endpoint := ctx.String(monitorCommandAttachFlag.Name) if client, err = utils.NewRemoteRPCClientFromString(endpoint); err != nil { utils.Fatalf("Unable to attach to gexp node: %v", err) } defer client.Close() // Retrieve all the available metrics and resolve the user pattens metrics, err := retrieveMetrics(client) if err != nil { utils.Fatalf("Failed to retrieve system metrics: %v", err) } monitored := resolveMetrics(metrics, ctx.Args()) if len(monitored) == 0 { list := expandMetrics(metrics, "") sort.Strings(list) if len(list) > 0 { utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - ")) } else { utils.Fatalf("No metrics collected by gexp (--%s).\n", utils.MetricsEnabledFlag.Name) } } sort.Strings(monitored) if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 { utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - ")) } // Create and configure the chart UI defaults if err := termui.Init(); err != nil { utils.Fatalf("Unable to initialize terminal UI: %v", err) } defer termui.Close() rows := len(monitored) if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max { rows = max } cols := (len(monitored) + rows - 1) / rows for i := 0; i < rows; i++ { termui.Body.AddRows(termui.NewRow()) } // Create each individual data chart footer := termui.NewPar("") footer.Block.Border = true footer.Height = 3 charts := make([]*termui.LineChart, len(monitored)) units := make([]int, len(monitored)) data := make([][]float64, len(monitored)) for i := 0; i < len(monitored); i++ { charts[i] = createChart((termui.TermHeight() - footer.Height) / rows) row := termui.Body.Rows[i%rows] row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i])) } termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) refreshCharts(client, monitored, data, units, charts, ctx, footer) termui.Body.Align() termui.Render(termui.Body) // Watch for various system events, and periodically refresh the charts termui.Handle("/sys/kbd/C-c", func(termui.Event) { termui.StopLoop() }) termui.Handle("/sys/wnd/resize", func(termui.Event) { termui.Body.Width = termui.TermWidth() for _, chart := range charts { chart.Height = (termui.TermHeight() - footer.Height) / rows } termui.Body.Align() termui.Render(termui.Body) }) go func() { tick := time.NewTicker(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second) for range tick.C { if refreshCharts(client, monitored, data, units, charts, ctx, footer) { termui.Body.Align() } termui.Render(termui.Body) } }() termui.Loop() return nil }