From 9183a54ea86e69a93b9e969abe57f2937fe2bf7e Mon Sep 17 00:00:00 2001 From: Tasos Bitsios Date: Tue, 11 Feb 2025 13:57:15 +0100 Subject: [PATCH] feat: show nodekit version within the TUI Co-authored-by: Michael Feher --- cmd/bootstrap.go | 4 ++-- cmd/root.go | 6 ++--- internal/algod/state.go | 6 ++++- ui/internal/test/state.go | 1 + ui/status.go | 21 +++++++++------- ui/status_test.go | 3 +++ ui/style/style.go | 24 +++++++++++++++++++ .../Test_StatusSnapshot/Syncing.golden | 2 +- 8 files changed, 52 insertions(+), 15 deletions(-) diff --git a/cmd/bootstrap.go b/cmd/bootstrap.go index 0198d070..988bcac1 100644 --- a/cmd/bootstrap.go +++ b/cmd/bootstrap.go @@ -89,7 +89,7 @@ var bootstrapCmd = &cobra.Command{ // Execute the TUI if we are caught up. // TODO: check the delta to see if it is necessary, if resp.JSON200.CatchupTime == 0 { - err = runTUI(RootCmd, dir, false) + err = runTUI(RootCmd, dir, false, RootCmd.Version) if err != nil { log.Fatal(err) } @@ -230,6 +230,6 @@ var bootstrapCmd = &cobra.Command{ } - return runTUI(RootCmd, dataDir, false) + return runTUI(RootCmd, dataDir, false, RootCmd.Version) }, } diff --git a/cmd/root.go b/cmd/root.go index d1432fc4..30bd61e2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -56,7 +56,7 @@ var ( }, Run: func(cmd *cobra.Command, args []string) { log.SetOutput(cmd.OutOrStdout()) - err := runTUI(cmd, algodData, IncentivesDisabled) + err := runTUI(cmd, algodData, IncentivesDisabled, cmd.Version) if err != nil { log.Fatal(err) } @@ -116,7 +116,7 @@ func Execute(version string, needsUpgrade bool) error { return RootCmd.Execute() } -func runTUI(cmd *cobra.Command, dataDir string, incentivesFlag bool) error { +func runTUI(cmd *cobra.Command, dataDir string, incentivesFlag bool, version string) error { if cmd == nil { return fmt.Errorf("cmd is nil") } @@ -128,7 +128,7 @@ func runTUI(cmd *cobra.Command, dataDir string, incentivesFlag bool) error { cobra.CheckErr(err) // Fetch the state and handle any creation errors - state, stateResponse, err := algod.NewStateModel(ctx, client, httpPkg, incentivesFlag) + state, stateResponse, err := algod.NewStateModel(ctx, client, httpPkg, incentivesFlag, version) utils.WithInvalidResponsesExplanations(err, stateResponse, cmd.UsageString()) cobra.CheckErr(err) diff --git a/internal/algod/state.go b/internal/algod/state.go index e0ffab00..f22cdd0b 100644 --- a/internal/algod/state.go +++ b/internal/algod/state.go @@ -13,6 +13,9 @@ import ( // including status, metrics, accounts, keys, and other configurations. type StateModel struct { + // Version indicates the version of the application. + Version string + // Status represents the current status of the algod node, // including network state and round information. Status Status @@ -56,7 +59,7 @@ type StateModel struct { // NewStateModel initializes and returns a new StateModel instance // along with an API response and potential error. -func NewStateModel(ctx context.Context, client api.ClientWithResponsesInterface, httpPkg api.HttpPkgInterface, incentivesDisabled bool) (*StateModel, api.ResponseInterface, error) { +func NewStateModel(ctx context.Context, client api.ClientWithResponsesInterface, httpPkg api.HttpPkgInterface, incentivesDisabled bool, version string) (*StateModel, api.ResponseInterface, error) { // Preload the node status status, response, err := NewStatus(ctx, client, httpPkg) if err != nil { @@ -79,6 +82,7 @@ func NewStateModel(ctx context.Context, client api.ClientWithResponsesInterface, Admin: true, Watching: true, + Version: version, Client: client, HttpPkg: httpPkg, Context: ctx, diff --git a/ui/internal/test/state.go b/ui/internal/test/state.go index 87835d22..66fc57ce 100644 --- a/ui/internal/test/state.go +++ b/ui/internal/test/state.go @@ -11,6 +11,7 @@ import ( func GetState(client api.ClientWithResponsesInterface) *algod.StateModel { sm := &algod.StateModel{ + Version: "vTest", Status: algod.Status{ State: algod.StableState, Version: "v-test", diff --git a/ui/status.go b/ui/status.go index a7a4d822..d65f8e88 100644 --- a/ui/status.go +++ b/ui/status.go @@ -113,14 +113,19 @@ func (m StatusViewModel) View() string { row3 := lipgloss.JoinHorizontal(lipgloss.Left, beginning, middle, end) - return style.WithTitle("Status", style.ApplyBorder(max(0, size-2), 5, "5").Render( - lipgloss.JoinVertical(lipgloss.Left, - row1, - "", - style.Cyan.Render(" -- "+strconv.Itoa(m.Data.Metrics.Window)+" round average --"), - row2, - row3, - ))) + return style.WithTitles( + "( "+style.Red.Render(fmt.Sprintf("Nodekit-%s", m.Data.Version))+" )", + lipgloss.NewStyle().Foreground(lipgloss.Color("5")).Render("Status"), + style.ApplyBorder(max(0, size-2), 5, "5").Render( + lipgloss.JoinVertical(lipgloss.Left, + row1, + "", + style.Cyan.Render(" -- "+strconv.Itoa(m.Data.Metrics.Window)+" round average --"), + row2, + row3, + ), + ), + ) } // MakeStatusViewModel constructs the model to be used in a tea.Program diff --git a/ui/status_test.go b/ui/status_test.go index 2822507b..5e6b2dfc 100644 --- a/ui/status_test.go +++ b/ui/status_test.go @@ -15,6 +15,7 @@ import ( var statusViewSnapshots = map[string]StatusViewModel{ "Syncing": { Data: &algod.StateModel{ + Version: "v0.0.0-test", Status: algod.Status{ LastRound: 1337, NeedsUpdate: true, @@ -31,6 +32,7 @@ var statusViewSnapshots = map[string]StatusViewModel{ }, "Hidden": { Data: &algod.StateModel{ + Version: "v0.0.0-test", Status: algod.Status{ LastRound: 1337, NeedsUpdate: true, @@ -47,6 +49,7 @@ var statusViewSnapshots = map[string]StatusViewModel{ }, "Loading": { Data: &algod.StateModel{ + Version: "v0.0.0-test", Status: algod.Status{ LastRound: 1337, NeedsUpdate: true, diff --git a/ui/style/style.go b/ui/style/style.go index a609666b..fdbac3de 100644 --- a/ui/style/style.go +++ b/ui/style/style.go @@ -59,6 +59,30 @@ func WithTitle(title string, view string) string { } return view } +func WithTitles(leftText string, rightText string, view string) string { + if leftText == "" || rightText == "" { + return view + } + + pad := 4 + controlWidth := lipgloss.Width(leftText) + lipgloss.Width(rightText) + + lines := strings.Split(view, "\n") + + if lipgloss.Width(view) >= controlWidth+(2*pad) { + line := lines[0] + lineWidth := lipgloss.Width(line) + lineLeft := ansi.Truncate(line, pad, "") + leftText + lineRight := rightText + TruncateLeft(line, lineWidth-pad) + + midTemplate := TruncateLeft(line, pad) + midLen := lineWidth - lipgloss.Width(lineLeft) - lipgloss.Width(lineRight) + midLine := ansi.Truncate(midTemplate, midLen, "") + lines[0] = lineLeft + midLine + lineRight + } + return strings.Join(lines, "\n") +} + func WithControls(nav string, view string) string { if nav == "" { return view diff --git a/ui/testdata/Test_StatusSnapshot/Syncing.golden b/ui/testdata/Test_StatusSnapshot/Syncing.golden index 0004a66d..063cde3e 100644 --- a/ui/testdata/Test_StatusSnapshot/Syncing.golden +++ b/ui/testdata/Test_StatusSnapshot/Syncing.golden @@ -1,4 +1,4 @@ -╭──Status────────────────────────────────────────────────────────────────────────────────╮ +╭───( Nodekit-v0.0.0-test )─────────────────────────────────────────────────────Status───╮ │ Latest Round: 1337 SYNCING │ │ │ │ -- 0 round average -- │