Fixed styling of the other tools

This commit is contained in:
Leon Mika 2022-06-27 16:05:59 +10:00
parent 809f9adfea
commit eadf8d1720
19 changed files with 231 additions and 105 deletions

View file

@ -7,14 +7,15 @@ import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/lmika/awstools/internal/common/ui/commandctrl" "github.com/lmika/awstools/internal/common/ui/commandctrl"
"github.com/lmika/awstools/internal/common/ui/logging" "github.com/lmika/awstools/internal/common/ui/logging"
"github.com/lmika/awstools/internal/slog-view/services/logreader"
"github.com/lmika/awstools/internal/slog-view/controllers" "github.com/lmika/awstools/internal/slog-view/controllers"
"github.com/lmika/awstools/internal/slog-view/services/logreader"
"github.com/lmika/awstools/internal/slog-view/ui" "github.com/lmika/awstools/internal/slog-view/ui"
"github.com/lmika/gopkgs/cli" "github.com/lmika/gopkgs/cli"
"os" "os"
) )
func main() { func main() {
var flagDebug = flag.String("debug", "", "file to log debug messages")
flag.Parse() flag.Parse()
if flag.NArg() == 0 { if flag.NArg() == 0 {
@ -24,7 +25,7 @@ func main() {
// Pre-determine if layout has dark background. This prevents calls for creating a list to hang. // Pre-determine if layout has dark background. This prevents calls for creating a list to hang.
lipgloss.HasDarkBackground() lipgloss.HasDarkBackground()
closeFn := logging.EnableLogging() closeFn := logging.EnableLogging(*flagDebug)
defer closeFn() defer closeFn()
service := logreader.NewService() service := logreader.NewService()

View file

@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"flag"
"fmt" "fmt"
"github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/ssm"
@ -18,10 +19,13 @@ import (
) )
func main() { func main() {
var flagDebug = flag.String("debug", "", "file to log debug messages")
flag.Parse()
// Pre-determine if layout has dark background. This prevents calls for creating a list to hang. // Pre-determine if layout has dark background. This prevents calls for creating a list to hang.
lipgloss.HasDarkBackground() lipgloss.HasDarkBackground()
closeFn := logging.EnableLogging() closeFn := logging.EnableLogging(*flagDebug)
defer closeFn() defer closeFn()
cfg, err := config.LoadDefaultConfig(context.Background()) cfg, err := config.LoadDefaultConfig(context.Background())

View file

@ -129,12 +129,12 @@ func (c *TableReadController) ExportCSV(filename string) tea.Cmd {
cw := csv.NewWriter(f) cw := csv.NewWriter(f)
defer cw.Flush() defer cw.Flush()
columns := resultSet.Columns columns := resultSet.Columns()
if err := cw.Write(columns); err != nil { if err := cw.Write(columns); err != nil {
return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename)) return events.Error(errors.Wrapf(err, "cannot export to '%v'", filename))
} }
row := make([]string, len(resultSet.Columns)) row := make([]string, len(columns))
for _, item := range resultSet.Items() { for _, item := range resultSet.Items() {
for i, col := range columns { for i, col := range columns {
row[i], _ = item.AttributeValueAsString(col) row[i], _ = item.AttributeValueAsString(col)

View file

@ -77,6 +77,7 @@ func (twc *TableWriteController) SetStringValue(idx int, key string) tea.Cmd {
twc.state.withResultSet(func(set *models.ResultSet) { twc.state.withResultSet(func(set *models.ResultSet) {
set.Items()[idx][key] = &types.AttributeValueMemberS{Value: value} set.Items()[idx][key] = &types.AttributeValueMemberS{Value: value}
set.SetDirty(idx, true) set.SetDirty(idx, true)
set.RefreshColumns()
}) })
return ResultSetUpdated{} return ResultSetUpdated{}
} }

View file

@ -1,11 +1,15 @@
package models package models
import "sort"
type ResultSet struct { type ResultSet struct {
TableInfo *TableInfo TableInfo *TableInfo
Query Queryable Query Queryable
Columns []string //Columns []string
items []Item items []Item
attributes []ItemAttribute attributes []ItemAttribute
columns []string
} }
type Queryable interface { type Queryable interface {
@ -75,3 +79,46 @@ func (rs *ResultSet) MarkedItems() []Item {
} }
return items return items
} }
func (rs *ResultSet) Columns() []string {
if rs.columns == nil {
rs.RefreshColumns()
}
return rs.columns
}
func (rs *ResultSet) RefreshColumns() {
seenColumns := make(map[string]int)
seenColumns[rs.TableInfo.Keys.PartitionKey] = 0
if rs.TableInfo.Keys.SortKey != "" {
seenColumns[rs.TableInfo.Keys.SortKey] = 1
}
for _, definedAttribute := range rs.TableInfo.DefinedAttributes {
if _, seen := seenColumns[definedAttribute]; !seen {
seenColumns[definedAttribute] = len(seenColumns)
}
}
otherColsRank := len(seenColumns)
for _, result := range rs.items {
for k := range result {
if _, isSeen := seenColumns[k]; !isSeen {
seenColumns[k] = otherColsRank
}
}
}
columns := make([]string, 0, len(seenColumns))
for k := range seenColumns {
columns = append(columns, k)
}
sort.Slice(columns, func(i, j int) bool {
if seenColumns[columns[i]] == seenColumns[columns[j]] {
return columns[i] < columns[j]
}
return seenColumns[columns[i]] < seenColumns[columns[j]]
})
rs.columns = columns
}

View file

@ -3,7 +3,6 @@ package tables
import ( import (
"context" "context"
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression" "github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
"sort"
"strings" "strings"
"github.com/lmika/awstools/internal/dynamo-browse/models" "github.com/lmika/awstools/internal/dynamo-browse/models"
@ -55,46 +54,47 @@ func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, expr
} }
// Get the columns // Get the columns
seenColumns := make(map[string]int) //seenColumns := make(map[string]int)
seenColumns[tableInfo.Keys.PartitionKey] = 0 //seenColumns[tableInfo.Keys.PartitionKey] = 0
if tableInfo.Keys.SortKey != "" { //if tableInfo.Keys.SortKey != "" {
seenColumns[tableInfo.Keys.SortKey] = 1 // seenColumns[tableInfo.Keys.SortKey] = 1
} //}
//
for _, definedAttribute := range tableInfo.DefinedAttributes { //for _, definedAttribute := range tableInfo.DefinedAttributes {
if _, seen := seenColumns[definedAttribute]; !seen { // if _, seen := seenColumns[definedAttribute]; !seen {
seenColumns[definedAttribute] = len(seenColumns) // seenColumns[definedAttribute] = len(seenColumns)
} // }
} //}
//
otherColsRank := len(seenColumns) //otherColsRank := len(seenColumns)
for _, result := range results { //for _, result := range results {
for k := range result { // for k := range result {
if _, isSeen := seenColumns[k]; !isSeen { // if _, isSeen := seenColumns[k]; !isSeen {
seenColumns[k] = otherColsRank // seenColumns[k] = otherColsRank
} // }
} // }
} //}
//
columns := make([]string, 0, len(seenColumns)) //columns := make([]string, 0, len(seenColumns))
for k := range seenColumns { //for k := range seenColumns {
columns = append(columns, k) // columns = append(columns, k)
} //}
sort.Slice(columns, func(i, j int) bool { //sort.Slice(columns, func(i, j int) bool {
if seenColumns[columns[i]] == seenColumns[columns[j]] { // if seenColumns[columns[i]] == seenColumns[columns[j]] {
return columns[i] < columns[j] // return columns[i] < columns[j]
} // }
return seenColumns[columns[i]] < seenColumns[columns[j]] // return seenColumns[columns[i]] < seenColumns[columns[j]]
}) //})
models.Sort(results, tableInfo) models.Sort(results, tableInfo)
resultSet := &models.ResultSet{ resultSet := &models.ResultSet{
TableInfo: tableInfo, TableInfo: tableInfo,
Query: expr, Query: expr,
Columns: columns, //Columns: columns,
} }
resultSet.SetItems(results) resultSet.SetItems(results)
resultSet.RefreshColumns()
return resultSet, nil return resultSet, nil
} }

View file

@ -89,11 +89,21 @@ func (m *Model) updateViewportToSelectedMessage() {
viewportContent := &strings.Builder{} viewportContent := &strings.Builder{}
tabWriter := tabwriter.NewWriter(viewportContent, 0, 1, 1, ' ', 0) tabWriter := tabwriter.NewWriter(viewportContent, 0, 1, 1, ' ', 0)
for _, colName := range m.currentResultSet.Columns {
seenColumns := make(map[string]struct{})
for _, colName := range m.currentResultSet.Columns() {
seenColumns[colName] = struct{}{}
if r := m.selectedItem.Renderer(colName); r != nil { if r := m.selectedItem.Renderer(colName); r != nil {
m.renderItem(tabWriter, "", colName, r) m.renderItem(tabWriter, "", colName, r)
} }
} }
for k, _ := range m.selectedItem {
if _, seen := seenColumns[k]; !seen {
if r := m.selectedItem.Renderer(k); r != nil {
m.renderItem(tabWriter, "", k, r)
}
}
}
tabWriter.Flush() tabWriter.Flush()
m.viewport.Width = m.w m.viewport.Width = m.w

View file

@ -48,11 +48,11 @@ type columnModel struct {
} }
func (cm columnModel) Len() int { func (cm columnModel) Len() int {
return len(cm.m.resultSet.Columns[cm.m.colOffset:]) return len(cm.m.resultSet.Columns()[cm.m.colOffset:])
} }
func (cm columnModel) Header(index int) string { func (cm columnModel) Header(index int) string {
return cm.m.resultSet.Columns[cm.m.colOffset+index] return cm.m.resultSet.Columns()[cm.m.colOffset+index]
} }
func New(uiStyles styles.Styles) *Model { func New(uiStyles styles.Styles) *Model {
@ -124,8 +124,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *Model) setLeftmostDisplayedColumn(newCol int) { func (m *Model) setLeftmostDisplayedColumn(newCol int) {
if newCol < 0 { if newCol < 0 {
m.colOffset = 0 m.colOffset = 0
} else if newCol >= len(m.resultSet.Columns) { } else if newCol >= len(m.resultSet.Columns()) {
m.colOffset = len(m.resultSet.Columns) - 1 m.colOffset = len(m.resultSet.Columns()) - 1
} else { } else {
m.colOffset = newCol m.colOffset = newCol
} }

View file

@ -50,7 +50,7 @@ func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) {
metaInfoStyle := style.Copy().Inherit(metaInfoStyle) metaInfoStyle := style.Copy().Inherit(metaInfoStyle)
sb := strings.Builder{} sb := strings.Builder{}
for i, colName := range mtr.resultSet.Columns[mtr.model.colOffset:] { for i, colName := range mtr.resultSet.Columns()[mtr.model.colOffset:] {
if i > 0 { if i > 0 {
sb.WriteString(style.Render("\t")) sb.WriteString(style.Render("\t"))
} }

View file

@ -0,0 +1,29 @@
package styles
import (
"github.com/charmbracelet/lipgloss"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
)
type Styles struct {
Frames frame.Style
StatusAndPrompt statusandprompt.Style
}
var DefaultStyles = Styles{
Frames: frame.Style{
ActiveTitle: lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
InactiveTitle: lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
},
StatusAndPrompt: statusandprompt.Style{
ModeLine: lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
},
}

View file

@ -2,6 +2,7 @@ package fullviewlinedetails
import ( import (
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
"github.com/lmika/awstools/internal/slog-view/models" "github.com/lmika/awstools/internal/slog-view/models"
"github.com/lmika/awstools/internal/slog-view/ui/linedetails" "github.com/lmika/awstools/internal/slog-view/ui/linedetails"
@ -14,10 +15,10 @@ type Model struct {
visible bool visible bool
} }
func NewModel(submodel tea.Model) *Model { func NewModel(submodel tea.Model, style frame.Style) *Model {
return &Model{ return &Model{
submodel: submodel, submodel: submodel,
lineDetails: linedetails.New(), lineDetails: linedetails.New(style),
} }
} }

View file

@ -10,13 +10,6 @@ import (
"github.com/lmika/awstools/internal/slog-view/models" "github.com/lmika/awstools/internal/slog-view/models"
) )
var (
activeHeaderStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#ffffff")).
Background(lipgloss.Color("#9c9c9c"))
)
type Model struct { type Model struct {
frameTitle frame.FrameTitle frameTitle frame.FrameTitle
viewport viewport.Model viewport viewport.Model
@ -27,11 +20,11 @@ type Model struct {
selectedItem *models.LogLine selectedItem *models.LogLine
} }
func New() *Model { func New(style frame.Style) *Model {
viewport := viewport.New(0, 0) viewport := viewport.New(0, 0)
viewport.SetContent("") viewport.SetContent("")
return &Model{ return &Model{
frameTitle: frame.NewFrameTitle("Item", false, activeHeaderStyle), frameTitle: frame.NewFrameTitle("Item", false, style),
viewport: viewport, viewport: viewport,
} }
} }

View file

@ -10,13 +10,6 @@ import (
"path/filepath" "path/filepath"
) )
var (
activeHeaderStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#ffffff")).
Background(lipgloss.Color("#9c9c9c"))
)
type Model struct { type Model struct {
frameTitle frame.FrameTitle frameTitle frame.FrameTitle
table table.Model table table.Model
@ -26,8 +19,8 @@ type Model struct {
w, h int w, h int
} }
func New() *Model { func New(style frame.Style) *Model {
frameTitle := frame.NewFrameTitle("File: ", true, activeHeaderStyle) frameTitle := frame.NewFrameTitle("File: ", true, style)
table := table.New(table.SimpleColumns{"level", "error", "message"}, 0, 0) table := table.New(table.SimpleColumns{"level", "error", "message"}, 0, 0)
return &Model{ return &Model{

View file

@ -6,38 +6,40 @@ import (
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
"github.com/lmika/awstools/internal/slog-view/controllers" "github.com/lmika/awstools/internal/slog-view/controllers"
"github.com/lmika/awstools/internal/slog-view/styles"
"github.com/lmika/awstools/internal/slog-view/ui/fullviewlinedetails" "github.com/lmika/awstools/internal/slog-view/ui/fullviewlinedetails"
"github.com/lmika/awstools/internal/slog-view/ui/linedetails" "github.com/lmika/awstools/internal/slog-view/ui/linedetails"
"github.com/lmika/awstools/internal/slog-view/ui/loglines" "github.com/lmika/awstools/internal/slog-view/ui/loglines"
) )
type Model struct { type Model struct {
controller *controllers.LogFileController controller *controllers.LogFileController
cmdController *commandctrl.CommandController cmdController *commandctrl.CommandController
root tea.Model root tea.Model
logLines *loglines.Model logLines *loglines.Model
lineDetails *linedetails.Model lineDetails *linedetails.Model
statusAndPrompt *statusandprompt.StatusAndPrompt statusAndPrompt *statusandprompt.StatusAndPrompt
fullViewLineDetails *fullviewlinedetails.Model fullViewLineDetails *fullviewlinedetails.Model
} }
func NewModel(controller *controllers.LogFileController, cmdController *commandctrl.CommandController) Model { func NewModel(controller *controllers.LogFileController, cmdController *commandctrl.CommandController) Model {
logLines := loglines.New() defaultStyles := styles.DefaultStyles
lineDetails := linedetails.New() logLines := loglines.New(defaultStyles.Frames)
lineDetails := linedetails.New(defaultStyles.Frames)
box := layout.NewVBox(layout.LastChildFixedAt(17), logLines, lineDetails) box := layout.NewVBox(layout.LastChildFixedAt(17), logLines, lineDetails)
fullViewLineDetails := fullviewlinedetails.NewModel(box) fullViewLineDetails := fullviewlinedetails.NewModel(box, defaultStyles.Frames)
statusAndPrompt := statusandprompt.New(fullViewLineDetails, "") statusAndPrompt := statusandprompt.New(fullViewLineDetails, "", defaultStyles.StatusAndPrompt)
root := layout.FullScreen(statusAndPrompt) root := layout.FullScreen(statusAndPrompt)
return Model{ return Model{
controller: controller, controller: controller,
cmdController: cmdController, cmdController: cmdController,
root: root, root: root,
statusAndPrompt: statusAndPrompt, statusAndPrompt: statusAndPrompt,
logLines: logLines, logLines: logLines,
lineDetails: lineDetails, lineDetails: lineDetails,
fullViewLineDetails: fullViewLineDetails, fullViewLineDetails: fullViewLineDetails,
} }
} }

View file

@ -0,0 +1,29 @@
package styles
import (
"github.com/charmbracelet/lipgloss"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
)
type Styles struct {
Frames frame.Style
StatusAndPrompt statusandprompt.Style
}
var DefaultStyles = Styles{
Frames: frame.Style{
ActiveTitle: lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#ffffff")).
Background(lipgloss.Color("#4479ff")),
InactiveTitle: lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
},
StatusAndPrompt: statusandprompt.Style{
ModeLine: lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
},
}

View file

@ -0,0 +1,29 @@
package styles
import (
"github.com/charmbracelet/lipgloss"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
)
type Styles struct {
Frames frame.Style
StatusAndPrompt statusandprompt.Style
}
var DefaultStyles = Styles{
Frames: frame.Style{
ActiveTitle: lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#ffffff")).
Background(lipgloss.Color("#c144ff")),
InactiveTitle: lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
},
StatusAndPrompt: statusandprompt.Style{
ModeLine: lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#d1d1d1")),
},
}

View file

@ -7,6 +7,7 @@ import (
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
"github.com/lmika/awstools/internal/ssm-browse/controllers" "github.com/lmika/awstools/internal/ssm-browse/controllers"
"github.com/lmika/awstools/internal/ssm-browse/styles"
"github.com/lmika/awstools/internal/ssm-browse/ui/ssmdetails" "github.com/lmika/awstools/internal/ssm-browse/ui/ssmdetails"
"github.com/lmika/awstools/internal/ssm-browse/ui/ssmlist" "github.com/lmika/awstools/internal/ssm-browse/ui/ssmlist"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -23,11 +24,11 @@ type Model struct {
} }
func NewModel(controller *controllers.SSMController, cmdController *commandctrl.CommandController) Model { func NewModel(controller *controllers.SSMController, cmdController *commandctrl.CommandController) Model {
ssmList := ssmlist.New() defaultStyles := styles.DefaultStyles
ssmdDetails := ssmdetails.New() ssmList := ssmlist.New(defaultStyles.Frames)
ssmdDetails := ssmdetails.New(defaultStyles.Frames)
statusAndPrompt := statusandprompt.New( statusAndPrompt := statusandprompt.New(
layout.NewVBox(layout.LastChildFixedAt(17), ssmList, ssmdDetails), layout.NewVBox(layout.LastChildFixedAt(17), ssmList, ssmdDetails), "", defaultStyles.StatusAndPrompt)
"")
cmdController.AddCommands(&commandctrl.CommandContext{ cmdController.AddCommands(&commandctrl.CommandContext{
Commands: map[string]commandctrl.Command{ Commands: map[string]commandctrl.Command{

View file

@ -11,13 +11,6 @@ import (
"strings" "strings"
) )
var (
activeHeaderStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#ffffff")).
Background(lipgloss.Color("#c144ff"))
)
type Model struct { type Model struct {
frameTitle frame.FrameTitle frameTitle frame.FrameTitle
viewport viewport.Model viewport viewport.Model
@ -28,11 +21,11 @@ type Model struct {
selectedItem *models.SSMParameter selectedItem *models.SSMParameter
} }
func New() *Model { func New(style frame.Style) *Model {
viewport := viewport.New(0, 0) viewport := viewport.New(0, 0)
viewport.SetContent("") viewport.SetContent("")
return &Model{ return &Model{
frameTitle: frame.NewFrameTitle("Item", false, activeHeaderStyle), frameTitle: frame.NewFrameTitle("Item", false, style),
viewport: viewport, viewport: viewport,
} }
} }

View file

@ -9,13 +9,6 @@ import (
table "github.com/lmika/go-bubble-table" table "github.com/lmika/go-bubble-table"
) )
var (
activeHeaderStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#ffffff")).
Background(lipgloss.Color("#c144ff"))
)
type Model struct { type Model struct {
frameTitle frame.FrameTitle frameTitle frame.FrameTitle
table table.Model table table.Model
@ -25,8 +18,8 @@ type Model struct {
w, h int w, h int
} }
func New() *Model { func New(style frame.Style) *Model {
frameTitle := frame.NewFrameTitle("SSM: /", true, activeHeaderStyle) frameTitle := frame.NewFrameTitle("SSM: /", true, style)
table := table.New(table.SimpleColumns{"name", "type", "value"}, 0, 0) table := table.New(table.SimpleColumns{"name", "type", "value"}, 0, 0)
return &Model{ return &Model{