Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/tui refactor #38

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func ReadFlags(cmd *cobra.Command) {
// Initialize your model with the current directory
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(currentDir, 20),
FilesSelector: modelutils.InitialModel(currentDir, 20, 20),
}
clearScreen()
// Bubble Tea program
Expand Down
4 changes: 3 additions & 1 deletion utils/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ func StartServer() {
// Initialize the file selector model with the directory argument
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(dir, pty.Window.Height-5), // Initialize the FilesSelector model with window height
FilesSelector: modelutils.InitialModel(dir, pty.Window.Height-5, pty.Window.Width-5),
Height: pty.Window.Height,
Width: pty.Window.Width,
}
if model.Error != nil {
wish.Println(s, model.Error.Error())
Expand Down
68 changes: 42 additions & 26 deletions utils/tui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/dyne/tgcom/utils/modfile"
"github.com/dyne/tgcom/utils/tui/modelutils"
)
Expand All @@ -20,6 +21,8 @@ type Model struct {
LabelType []bool
CurrentDir string // Current directory for file selection
Error error
Width int
Height int

// Models for different selection steps
FilesSelector modelutils.FilesSelector
Expand All @@ -46,17 +49,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

switch msg := msg.(type) {
case tea.KeyMsg:
if m.State == "Final" {
return m, tea.Quit
}
case applyChangesMsg:
if msg.err != nil {
m.Error = msg.err
}
m.State = "Final"
return m, nil

case tea.KeyMsg:
if m.State == "Final" {
return m, tea.Quit
}
}

switch m.State {
Expand All @@ -77,11 +79,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
Speed: "",
}
m.State = "ActionSelection"
m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[0]), m.SpeedSelector.Selected)
m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[0]), m.SpeedSelector.Selected, m.Width, m.Height)
} else {

m.State = "ModeSelection"
m.SpeedSelector = modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", "")
m.SpeedSelector = modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", "", m.Width, m.Height)
}
}
return m, cmd
Expand All @@ -95,7 +96,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
if m.SpeedSelector.Done {
m.State = "ActionSelection"
m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[0]), m.SpeedSelector.Selected)
m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[0]), m.SpeedSelector.Selected, m.Width, m.Height)
}
return m, cmd

Expand All @@ -113,18 +114,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.ActionSelector.Done = false
m.Actions = m.Actions[:len(m.Actions)-1]
m.State = "ActionSelection"
m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[len(m.Actions)]), m.SpeedSelector.Selected)

m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[len(m.Actions)]), m.SpeedSelector.Selected, m.Width, m.Height)
}
}
if m.ActionSelector.Done {
m.Actions = append(m.Actions, m.ActionSelector.Selected)
if len(m.Actions) == len(m.Files) {
m.State = "LabelInput"
m.LabelInput = modelutils.NewLabelInput(filepath.Base(m.Files[0]))
m.LabelInput = modelutils.NewLabelInput(filepath.Base(m.Files[0]), m.Width, m.Height)
} else {
m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[len(m.Actions)]), m.SpeedSelector.Selected)

m.ActionSelector = modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, filepath.Base(m.Files[len(m.Actions)]), m.SpeedSelector.Selected, m.Width, m.Height)
}
}
return m, cmd
Expand All @@ -133,6 +132,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.ActionSelector = newActionSelector.(modelutils.ModeSelector)
if m.ActionSelector.Back {
if len(m.Files) == 1 {
if !m.FilesSelector.MultipleSelection {
m.FilesSelector.FilesPath = []string{}
}
m.State = "FileSelection"
m.FilesSelector.Done = false
} else {
Expand All @@ -146,7 +148,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.Actions = append(m.Actions, m.ActionSelector.Selected)
}
m.State = "LabelInput"
m.LabelInput = modelutils.NewLabelInput("")
m.LabelInput = modelutils.NewLabelInput("", m.Width, m.Height)
}
return m, cmd
}
Expand All @@ -167,7 +169,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.Labels = m.Labels[:len(m.Labels)-1]
m.LabelType = m.LabelType[:len(m.LabelType)-1]
m.State = "LabelInput"
m.LabelInput = modelutils.NewLabelInput(filepath.Base(m.Files[len(m.Labels)]))
m.LabelInput = modelutils.NewLabelInput(filepath.Base(m.Files[len(m.Labels)]), m.Width, m.Height)
}
}
if m.LabelInput.Done {
Expand All @@ -181,8 +183,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.State = "ApplyChanges"
return m, m.applyChanges()
} else {
m.LabelInput = modelutils.NewLabelInput(filepath.Base(m.Files[len(m.Labels)]))

m.LabelInput = modelutils.NewLabelInput(filepath.Base(m.Files[len(m.Labels)]), m.Width, m.Height)
}
}
return m, cmd
Expand Down Expand Up @@ -218,24 +219,39 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

// View renders the view based on the current state
func (m Model) View() string {
var rightPane string
fileSelectionPane := m.FilesSelector.View()
var halfWidth int
switch m.State {
case "FileSelection":
return m.FilesSelector.View()
return fileSelectionPane
case "ModeSelection":
return m.SpeedSelector.View()
halfWidth = m.SpeedSelector.Width
rightPane = m.SpeedSelector.View()
case "ActionSelection":
return m.ActionSelector.View()
halfWidth = m.ActionSelector.Width
rightPane = m.ActionSelector.View()
case "LabelInput":
return m.LabelInput.View()
halfWidth = m.LabelInput.Width
rightPane = m.LabelInput.View()
case "ApplyChanges":
return "Applying changes..."
rightPane = "Applying changes..."
case "Final":
if m.Error != nil {
return modelutils.Paint("red").Render(fmt.Sprintf("An error occurred: %v\nPress any key to exit.", m.Error))
rightPane = modelutils.Paint("red").Render(fmt.Sprintf("An error occurred: %v\nPress any key to exit.", m.Error))
} else {
rightPane = "Changes applied successfully!\nPress any key to exit."
}
return "Changes applied successfully!\nPress any key to exit."
}
return ""

// Use a style for the layout
layout := lipgloss.JoinHorizontal(
lipgloss.Top,
lipgloss.NewStyle().Width(halfWidth).Render(fileSelectionPane),
lipgloss.NewStyle().Width(halfWidth).Render(rightPane),
)

return layout
}

// applyChanges applies changes to selected files based on user inputs
Expand Down
64 changes: 26 additions & 38 deletions utils/tui/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ import (
)

func TestModel(t *testing.T) {
tempDir := t.TempDir()
tempFile := filepath.Join(tempDir, "file.txt")
_, err := os.Create(tempFile)
assert.NoError(t, err)

t.Run("Init", func(t *testing.T) {
model := Model{FilesSelector: modelutils.InitialModel(".", 10)}
model := Model{FilesSelector: modelutils.InitialModel(tempDir, 10, 10)}
cmd := model.Init()
assert.Nil(t, cmd)
})
Expand All @@ -32,9 +37,10 @@ func TestModel(t *testing.T) {
name: "FileSelection to ModeSelection",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(tempDir, 10, 10),
},
setup: func(m *Model) {
m.FilesSelector.MultipleSelection = true
m.FilesSelector.FilesPath = []string{"path/test/file1", "path/test/file2"}

},
Expand All @@ -51,16 +57,14 @@ func TestModel(t *testing.T) {
name: "FileSelection to ActionSelection",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(tempDir, 10, 10),
},
setup: func(m *Model) {
FilippoTrotter marked this conversation as resolved.
Show resolved Hide resolved
m.FilesSelector.FilesPath = []string{"path/test/file1"}

},
msg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}},
msg: tea.KeyMsg{Type: tea.KeyEnter},
verify: func(t *testing.T, m Model) {
assert.True(t, m.FilesSelector.Done)
assert.Contains(t, m.Files, "path/test/file1")
assert.Contains(t, m.Files, tempFile)
assert.Equal(t, "ActionSelection", m.State)

},
Expand All @@ -69,7 +73,7 @@ func TestModel(t *testing.T) {
name: "No file selected",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(tempDir, 10, 10),
},
msg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}},
verify: func(t *testing.T, m Model) {
Expand All @@ -82,7 +86,7 @@ func TestModel(t *testing.T) {
name: "ModeSelection to ActionSelection",
model: Model{
State: "ModeSelection",
SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", ""),
SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", "", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEnter},
Expand All @@ -97,7 +101,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "ActionSelection",
SpeedSelector: modelutils.ModeSelector{Selected: "Slow mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode"),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEnter},
Expand All @@ -111,7 +115,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "ActionSelection",
SpeedSelector: modelutils.ModeSelector{Selected: "Fast mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Fast mode"),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Fast mode", 10, 10),
Files: []string{"file1.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEnter},
Expand All @@ -125,7 +129,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "ActionSelection",
SpeedSelector: modelutils.ModeSelector{Selected: "Slow mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode"),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
Actions: []string{"test"},
},
Expand Down Expand Up @@ -156,8 +160,8 @@ func TestModel(t *testing.T) {
name: "ModeSelection to FileSelection",
model: Model{
State: "ModeSelection",
FilesSelector: modelutils.InitialModel(".", 10),
SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", ""),
FilesSelector: modelutils.InitialModel(tempDir, 10, 10),
SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", "", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEsc},
Expand All @@ -174,9 +178,9 @@ func TestModel(t *testing.T) {
name: "ActionSelection to FileSelection",
model: Model{
State: "ActionSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(tempDir, 10, 10),
SpeedSelector: modelutils.ModeSelector{Selected: "Fast mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", ""),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "", 10, 10),
Files: []string{"file1.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEsc},
Expand All @@ -192,7 +196,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "ActionSelection",
SpeedSelector: modelutils.ModeSelector{Selected: "Fast mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", ""),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEsc},
Expand All @@ -208,7 +212,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "ActionSelection",
SpeedSelector: modelutils.ModeSelector{Selected: "Slow mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", ""),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
},
msg: tea.KeyMsg{Type: tea.KeyEsc},
Expand All @@ -223,7 +227,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "ActionSelection",
SpeedSelector: modelutils.ModeSelector{Selected: "Slow mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode"),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode", 10, 10),
Files: []string{"file1.txt", "file2.txt"},
Actions: []string{"test", "comment"},
},
Expand All @@ -240,7 +244,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "LabelInput",
SpeedSelector: modelutils.ModeSelector{Selected: "Fast mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Fast mode"),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Fast mode", 10, 10),
LabelInput: modelutils.LabelInput{Input: "1-3"},
Files: []string{"file1.txt"},
},
Expand All @@ -255,7 +259,7 @@ func TestModel(t *testing.T) {
model: Model{
State: "LabelInput",
SpeedSelector: modelutils.ModeSelector{Selected: "Slow mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode"),
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Slow mode", 10, 10),
LabelInput: modelutils.LabelInput{Input: "1-3"},
Files: []string{"file1.txt", "file2.txt"},
Actions: []string{"test", "comment"},
Expand Down Expand Up @@ -424,25 +428,9 @@ func TestModel(t *testing.T) {
tests := []viewTest{
{
name: "FileSelection View",
model: Model{State: "FileSelection", FilesSelector: modelutils.InitialModel(".", 10)},
model: Model{State: "FileSelection", FilesSelector: modelutils.InitialModel(tempDir, 10, 10)},
expected: "Select the files you want to modify",
},
{
name: "ModeSelection View",
model: Model{State: "ModeSelection", SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", "")},
expected: "Select 'Fast mode'",
},
{
name: "ActionSelection View",
model: Model{State: "ActionSelection", ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", "Fast mode")},
expected: "Select action",
},
{
name: "LabelInput View",
model: Model{State: "LabelInput", LabelInput: modelutils.NewLabelInput("")},
expected: "Type below the section to modify",
},

{
name: "Final View with Error",
model: Model{State: "Final", Error: fmt.Errorf("test error")},
Expand Down
Loading
Loading