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

Refactor: Live Mode and General Improvements #65

Merged
merged 53 commits into from
Jan 6, 2025
Merged
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b5da129
Separate the header
canack Jun 26, 2024
5b4f1dc
Make the code better
canack Jun 26, 2024
44e7f4a
Merge branch 'refs/heads/main' into feature/i31-live-mode
canack Jun 26, 2024
c86f842
small improvements for better user experience
canack Jun 26, 2024
ed66581
Make the code better
canack Jun 27, 2024
84c66e2
Make the code better
canack Jun 29, 2024
0080dcb
Refactor the code
canack Jul 14, 2024
1d3487b
Architectural changes
canack Jul 30, 2024
b7eae64
Minor resizing
canack Aug 3, 2024
71bcbd9
Minor enhancements
canack Aug 3, 2024
c0a8dbc
Fix blocking calls
canack Aug 4, 2024
013baf6
Small code changes
canack Aug 4, 2024
980d167
Clean empty table
canack Aug 6, 2024
c89755a
Implement live mode
canack Aug 6, 2024
8b64ae2
Show running workflow's duration
canack Aug 6, 2024
0be0559
Custom interval support for live mode
canack Aug 6, 2024
34334fe
Change trigger pane colors
canack Aug 6, 2024
ca9658a
remove unused code
canack Aug 6, 2024
16afd93
remove unnecessary file
canack Aug 6, 2024
1c1e23a
first steps of enhanced ui
canack Aug 8, 2024
d073ee7
Start to use skeleton package
canack Sep 1, 2024
e54e3e5
Enhance UI
canack Sep 2, 2024
fc1445a
Improve code style
canack Sep 2, 2024
660ccd9
Revert "Improve code style"
canack Sep 2, 2024
7b538bc
Reapply "Improve code style"
canack Sep 2, 2024
8dc8680
Make architecture basic
canack Sep 2, 2024
598ef3d
Rearrange methods and delete unused code
canack Sep 2, 2024
648bcf5
Rearrange code
canack Sep 2, 2024
371eada
Fix refresh history after trigger
canack Sep 2, 2024
87f1532
Remove unnecessary spinner and fix self update
canack Sep 5, 2024
4b01fd0
Made listeners common
canack Sep 5, 2024
39b271b
Minor improvements
canack Sep 5, 2024
65c42cd
Rename and make private some variables
canack Sep 5, 2024
99f1187
Rename & minor improvements
canack Sep 7, 2024
a9110ab
Minor improvements
canack Sep 7, 2024
206ba46
Update gitignore
canack Dec 17, 2024
a9bc5ef
Upgrade dependencies
canack Dec 17, 2024
e11dbd3
Remove "Launch the selected option" helper. No need to show it always.
canack Dec 17, 2024
8efeab6
Make confirmation message better
canack Dec 17, 2024
1460ed0
Improve updater mechanism, remove obsolete and unused code for better…
canack Dec 17, 2024
8ca134e
remove go.work.sum
canack Dec 17, 2024
f168b6a
Merge branch 'main' into feature/i31-live-mode
canack Dec 17, 2024
0e11431
Upgrade the dependencies
canack Dec 18, 2024
ac225e2
Upgrade termkit/skeleton to v0.2.0 and add GetRepositoryBranches func…
canack Jan 5, 2025
2a0de10
Refactor terminal handler models and improve UI components
canack Jan 5, 2025
f5f501e
Update go.mod and go.sum to include new dependencies and upgrade exis…
canack Jan 6, 2025
b57e81e
Enhance GitHub workflow handling and state management
canack Jan 6, 2025
9fa580e
Refactor GitHub repository and workflow handling for improved perform…
canack Jan 6, 2025
ac4de9a
Minor changes
canack Jan 6, 2025
7456da0
Update input size
canack Jan 6, 2025
cac6e63
update readme
canack Jan 6, 2025
1f7227d
Update go version
canack Jan 6, 2025
d9e0412
Minor changes
canack Jan 6, 2025
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
Prev Previous commit
Next Next commit
Refactor the code
canack committed Jul 14, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 0080dcbd6156d48e5fd4acb03f92418ef74501b1
12 changes: 6 additions & 6 deletions internal/terminal/handler/error/error.go
Original file line number Diff line number Diff line change
@@ -45,23 +45,23 @@ func SetupModelError() ModelError {

func (m *ModelError) View() string {
var windowStyle = lipgloss.NewStyle().BorderStyle(lipgloss.RoundedBorder())

width := hdltypes.NewTerminalViewport().Width - 4
doc := strings.Builder{}
if m.HaveError() {
windowStyle = ts.WindowStyleError.Width(*hdltypes.ScreenWidth)
windowStyle = ts.WindowStyleError.Width(width)
doc.WriteString(windowStyle.Render(m.ViewError()))
return doc.String()
}

switch m.messageType {
case MessageTypeDefault:
windowStyle = ts.WindowStyleDefault.Width(*hdltypes.ScreenWidth)
windowStyle = ts.WindowStyleDefault.Width(width)
case MessageTypeProgress:
windowStyle = ts.WindowStyleProgress.Width(*hdltypes.ScreenWidth)
windowStyle = ts.WindowStyleProgress.Width(width)
case MessageTypeSuccess:
windowStyle = ts.WindowStyleSuccess.Width(*hdltypes.ScreenWidth)
windowStyle = ts.WindowStyleSuccess.Width(width)
default:
windowStyle = ts.WindowStyleDefault.Width(*hdltypes.ScreenWidth)
windowStyle = ts.WindowStyleDefault.Width(width)
}

doc.WriteString(windowStyle.Render(m.ViewMessage()))
57 changes: 35 additions & 22 deletions internal/terminal/handler/ghrepository/ghrepository.go
Original file line number Diff line number Diff line change
@@ -4,9 +4,6 @@ import (
"context"
"errors"
"fmt"
"strconv"
"strings"

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/table"
@@ -19,7 +16,11 @@ import (
hdlerror "github.com/termkit/gama/internal/terminal/handler/error"
"github.com/termkit/gama/internal/terminal/handler/taboptions"
hdltypes "github.com/termkit/gama/internal/terminal/handler/types"
ts "github.com/termkit/gama/internal/terminal/style"
"github.com/termkit/gama/pkg/browser"
"strconv"
"strings"
"time"
)

type ModelGithubRepository struct {
@@ -49,10 +50,6 @@ type ModelGithubRepository struct {
textInput textinput.Model
}

var baseStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240"))

func SetupModelGithubRepository(githubUseCase gu.UseCase) *ModelGithubRepository {
var tableRowsGithubRepository []table.Row

@@ -127,9 +124,19 @@ func SetupModelGithubRepository(githubUseCase gu.UseCase) *ModelGithubRepository
}
}

func (m *ModelGithubRepository) Init() tea.Cmd {
go m.syncRepositories(m.syncRepositoriesContext)
type UpdateSpinnerMsg string

func (m *ModelGithubRepository) tickSpinner() tea.Cmd {
t := time.NewTimer(time.Millisecond * 200)
return func() tea.Msg {
select {
case <-t.C:
return UpdateSpinnerMsg("tick")
}
}
}

func (m *ModelGithubRepository) Init() tea.Cmd {
openInBrowser := func() {
m.modelError.SetProgressMessage("Opening in browser...")

@@ -145,10 +152,10 @@ func (m *ModelGithubRepository) Init() tea.Cmd {

m.modelTabOptions.AddOption("Open in browser", openInBrowser)

return tea.Batch(m.modelTabOptions.Init())
return tea.Batch(m.modelTabOptions.Init(), m.syncRepositories(m.syncRepositoriesContext), m.tickSpinner())
}

func (m *ModelGithubRepository) Update(msg tea.Msg) (*ModelGithubRepository, tea.Cmd) {
func (m *ModelGithubRepository) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
var cmd tea.Cmd

@@ -169,6 +176,8 @@ func (m *ModelGithubRepository) Update(msg tea.Msg) (*ModelGithubRepository, tea
m.searchTableGithubRepository.GotoTop()
m.searchTableGithubRepository.SetCursor(0)
}
case UpdateSpinnerMsg:
return m, m.tickSpinner()
}

m.textInput, cmd = m.textInput.Update(textInputMsg)
@@ -191,29 +200,32 @@ func (m *ModelGithubRepository) Update(msg tea.Msg) (*ModelGithubRepository, tea
}

func (m *ModelGithubRepository) View() string {
termWidth := m.Viewport.Width
termHeight := m.Viewport.Height
var baseStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240")).MarginLeft(1)

helpWindowStyle := ts.WindowStyleHelp.Width(m.Viewport.Width - 4)

var tableWidth int
for _, t := range tableColumnsGithubRepository {
tableWidth += t.Width
}

newTableColumns := tableColumnsGithubRepository
widthDiff := termWidth - tableWidth
widthDiff := m.Viewport.Width - tableWidth
if widthDiff > 0 {
newTableColumns[0].Width += widthDiff - 15
newTableColumns[0].Width += widthDiff - 12
m.tableGithubRepository.SetColumns(newTableColumns)
m.tableGithubRepository.SetHeight(termHeight - 20)
m.tableGithubRepository.SetHeight(m.Viewport.Height - 19)
}

doc := strings.Builder{}
doc.WriteString(baseStyle.Render(m.tableGithubRepository.View()))

return lipgloss.JoinVertical(lipgloss.Top, doc.String(), m.viewSearchBar(), m.modelTabOptions.View())
return lipgloss.JoinVertical(lipgloss.Top, doc.String(), m.viewSearchBar(), m.modelTabOptions.View(), m.ViewStatus(), helpWindowStyle.Render(m.ViewHelp()))
}

func (m *ModelGithubRepository) syncRepositories(ctx context.Context) {
func (m *ModelGithubRepository) syncRepositories(ctx context.Context) tea.Cmd {
m.modelError.ResetError() // reset previous errors
m.modelTabOptions.SetStatus(taboptions.OptionWait)
m.modelError.SetProgressMessage("Fetching repositories...")
@@ -228,18 +240,18 @@ func (m *ModelGithubRepository) syncRepositories(ctx context.Context) {
Sort: domain.SortByUpdated,
})
if errors.Is(err, context.Canceled) {
return
return nil
} else if err != nil {
m.modelError.SetError(err)
m.modelError.SetErrorMessage("Repositories cannot be listed")
return
return nil
}

if len(repositories.Repositories) == 0 {
m.modelTabOptions.SetStatus(taboptions.OptionNone)
m.modelError.SetDefaultMessage("No repositories found")
m.textInput.Blur()
return
return nil
}

tableRowsGithubRepository := make([]table.Row, 0, len(repositories.Repositories))
@@ -260,6 +272,7 @@ func (m *ModelGithubRepository) syncRepositories(ctx context.Context) {
m.textInput.Focus()
m.modelError.SetSuccessMessage("Repositories fetched")
go m.Update(m) // update model
return nil
}

func (m *ModelGithubRepository) handleTableInputs(_ context.Context) {
@@ -284,7 +297,7 @@ func (m *ModelGithubRepository) viewSearchBar() string {
windowStyle := lipgloss.NewStyle().
Border(lipgloss.NormalBorder()).
Padding(0, 1).
Width(*hdltypes.ScreenWidth - 2)
Width(m.Viewport.Width - 4).MarginLeft(1)

// Build the options list
doc := strings.Builder{}
20 changes: 9 additions & 11 deletions internal/terminal/handler/ghtrigger/ghtrigger.go
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import (
"github.com/termkit/gama/internal/terminal/handler/ghworkflowhistory"
"github.com/termkit/gama/internal/terminal/handler/header"
hdltypes "github.com/termkit/gama/internal/terminal/handler/types"
ts "github.com/termkit/gama/internal/terminal/style"
"github.com/termkit/gama/pkg/workflow"
"slices"
"strings"
@@ -101,7 +102,7 @@ func (m *ModelGithubTrigger) Init() tea.Cmd {
return tea.Batch(textinput.Blink)
}

func (m *ModelGithubTrigger) Update(msg tea.Msg) (*ModelGithubTrigger, tea.Cmd) {
func (m *ModelGithubTrigger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.SelectedRepository.WorkflowName == "" {
m.modelError.Reset()
m.modelError.SetDefaultMessage("No workflow selected.")
@@ -342,6 +343,8 @@ func (m *ModelGithubTrigger) inputController(_ context.Context) {

func (m *ModelGithubTrigger) View() string {
baseStyle := lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder())
helpWindowStyle := ts.WindowStyleHelp.Width(m.Viewport.Width - 4)

if m.triggerFocused {
baseStyle = baseStyle.BorderForeground(lipgloss.Color("240"))
} else {
@@ -381,7 +384,7 @@ func (m *ModelGithubTrigger) View() string {
}

return lipgloss.JoinVertical(lipgloss.Top, doc.String(),
lipgloss.JoinHorizontal(lipgloss.Top, selector, m.triggerButton()))
lipgloss.JoinHorizontal(lipgloss.Top, selector, m.triggerButton()), m.modelError.View(), helpWindowStyle.Render(m.ViewHelp()))
}

func (m *ModelGithubTrigger) syncWorkflowContent(ctx context.Context) {
@@ -619,7 +622,7 @@ func (m *ModelGithubTrigger) emptySelector() string {
windowStyle := lipgloss.NewStyle().
Border(lipgloss.NormalBorder()).
Padding(0, 1).
Width(*hdltypes.ScreenWidth - 13)
Width(m.Viewport.Width - 13)

// Build the options list
doc := strings.Builder{}
@@ -632,14 +635,9 @@ func (m *ModelGithubTrigger) inputSelector() string {
windowStyle := lipgloss.NewStyle().
Border(lipgloss.NormalBorder()).
Padding(0, 1).
Width(*hdltypes.ScreenWidth - 13)

// Build the options list
doc := strings.Builder{}

doc.WriteString(m.textInput.View())
Width(m.Viewport.Width - 13).MarginLeft(2)

return windowStyle.Render(doc.String())
return windowStyle.Render(m.textInput.View())
}

// optionSelector renders the options list
@@ -649,7 +647,7 @@ func (m *ModelGithubTrigger) optionSelector() string {
windowStyle := lipgloss.NewStyle().
Border(lipgloss.NormalBorder()).
Padding(0, 1).
Width(*hdltypes.ScreenWidth - 13)
Width(m.Viewport.Width - 13).MarginLeft(1)

// Define styles for selected and unselected options
selectedOptionStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("120")).Padding(0, 1)
7 changes: 5 additions & 2 deletions internal/terminal/handler/ghworkflow/ghworkflow.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
ts "github.com/termkit/gama/internal/terminal/style"
"sort"
"strings"

@@ -87,7 +88,7 @@ func (m *ModelGithubWorkflow) Init() tea.Cmd {
return nil
}

func (m *ModelGithubWorkflow) Update(msg tea.Msg) (*ModelGithubWorkflow, tea.Cmd) {
func (m *ModelGithubWorkflow) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd

if m.lastRepository != m.SelectedRepository.RepositoryName {
@@ -112,6 +113,8 @@ func (m *ModelGithubWorkflow) View() string {
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240"))

helpWindowStyle := ts.WindowStyleHelp.Width(m.Viewport.Width - 4)

termWidth := m.Viewport.Width
termHeight := m.Viewport.Height

@@ -132,7 +135,7 @@ func (m *ModelGithubWorkflow) View() string {
doc.WriteString(style.Render(m.tableTriggerableWorkflow.View()))
doc.WriteString("\n\n\n")

return doc.String()
return lipgloss.JoinVertical(lipgloss.Top, doc.String(), m.ViewStatus(), helpWindowStyle.Render(m.ViewHelp()))
}

func (m *ModelGithubWorkflow) syncTriggerableWorkflows(ctx context.Context) {
14 changes: 8 additions & 6 deletions internal/terminal/handler/ghworkflowhistory/ghworkflowhistory.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
ts "github.com/termkit/gama/internal/terminal/style"
"strings"
"time"

@@ -173,7 +174,7 @@ func (m *ModelGithubWorkflowHistory) setupOptions() {
m.modelTabOptions.AddOption("Cancel workflow", cancelWorkflow)
}

func (m *ModelGithubWorkflowHistory) Update(msg tea.Msg) (*ModelGithubWorkflowHistory, tea.Cmd) {
func (m *ModelGithubWorkflowHistory) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.lastRepository != m.SelectedRepository.RepositoryName {
m.tableReady = false
m.cancelSyncWorkflowHistory() // cancel previous sync
@@ -269,7 +270,8 @@ func (m *ModelGithubWorkflowHistory) syncWorkflowHistory(ctx context.Context) {
func (m *ModelGithubWorkflowHistory) View() string {
var baseStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240"))
BorderForeground(lipgloss.Color("240")).MarginLeft(1)
helpWindowStyle := ts.WindowStyleHelp.Width(m.Viewport.Width - 4)

termWidth := m.Viewport.Width
termHeight := m.Viewport.Height
@@ -284,20 +286,20 @@ func (m *ModelGithubWorkflowHistory) View() string {

if widthDiff > 0 {
if m.updateRound%2 == 0 {
newTableColumns[0].Width += widthDiff - 19
newTableColumns[0].Width += widthDiff - 16
} else {
newTableColumns[1].Width += widthDiff - 19
newTableColumns[1].Width += widthDiff - 16
}
m.updateRound++
m.tableWorkflowHistory.SetColumns(newTableColumns)
}

m.tableWorkflowHistory.SetHeight(termHeight - 17)
m.tableWorkflowHistory.SetHeight(termHeight - 16)

doc := strings.Builder{}
doc.WriteString(baseStyle.Render(m.tableWorkflowHistory.View()))

return lipgloss.JoinVertical(lipgloss.Top, doc.String(), m.modelTabOptions.View())
return lipgloss.JoinVertical(lipgloss.Top, doc.String(), m.modelTabOptions.View(), m.ViewStatus(), helpWindowStyle.Render(m.ViewHelp()))
}

func (m *ModelGithubWorkflowHistory) ViewStatus() string {
162 changes: 38 additions & 124 deletions internal/terminal/handler/handler.go
Original file line number Diff line number Diff line change
@@ -2,68 +2,61 @@ package handler

import (
"fmt"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
gu "github.com/termkit/gama/internal/github/usecase"
hdlerror "github.com/termkit/gama/internal/terminal/handler/error"
hdlgithubrepo "github.com/termkit/gama/internal/terminal/handler/ghrepository"
hdltrigger "github.com/termkit/gama/internal/terminal/handler/ghtrigger"
hdlWorkflow "github.com/termkit/gama/internal/terminal/handler/ghworkflow"
hdlworkflowhistory "github.com/termkit/gama/internal/terminal/handler/ghworkflowhistory"
"github.com/termkit/gama/internal/terminal/handler/header"
hdlinfo "github.com/termkit/gama/internal/terminal/handler/information"
hdlskeleton "github.com/termkit/gama/internal/terminal/handler/skeleton"
hdltypes "github.com/termkit/gama/internal/terminal/handler/types"
ts "github.com/termkit/gama/internal/terminal/style"
pkgversion "github.com/termkit/gama/pkg/version"
"strings"
"time"
)

type model struct {
// models
viewport *viewport.Model

modelError *hdlerror.ModelError
modelHeader *header.Header
modelInfo *hdlinfo.ModelInfo
modelGithubRepository *hdlgithubrepo.ModelGithubRepository
modelWorkflow *hdlWorkflow.ModelGithubWorkflow
modelWorkflowHistory *hdlworkflowhistory.ModelGithubWorkflowHistory
modelTrigger *hdltrigger.ModelGithubTrigger
viewport *viewport.Model
modelSkeleton *hdlskeleton.Skeleton

// keymap
keys keyMap
}

func SetupTerminal(githubUseCase gu.UseCase, version pkgversion.Version) tea.Model {
// setup models
hdlModelError := hdlerror.SetupModelError()
hdlModelHeader := header.NewHeader()
hdlModelInfo := hdlinfo.SetupModelInfo(githubUseCase, version)
hdlModelGithubRepository := hdlgithubrepo.SetupModelGithubRepository(githubUseCase)
hdlModelWorkflowHistory := hdlworkflowhistory.SetupModelGithubWorkflowHistory(githubUseCase)
hdlModelWorkflow := hdlWorkflow.SetupModelGithubWorkflow(githubUseCase)
hdlModelTrigger := hdltrigger.SetupModelGithubTrigger(githubUseCase)

hdlModelHeader.AddCommonHeader("Info", ts.TitleStyleInactive, ts.TitleStyleActive)
hdlModelHeader.AddCommonHeader("Repository", ts.TitleStyleInactive, ts.TitleStyleActive)
hdlModelHeader.AddCommonHeader("Workflow History", ts.TitleStyleInactive, ts.TitleStyleActive)
hdlModelHeader.AddCommonHeader("Workflow", ts.TitleStyleInactive, ts.TitleStyleActive)
hdlModelHeader.AddCommonHeader("Trigger", ts.TitleStyleInactive, ts.TitleStyleActive)
hdlModelHeader.SetSpecialHeader("GAMA", time.Millisecond*500, ts.TitleStyleLiveModeOn, ts.TitleStyleLiveModeOff, ts.TitleStyleDisabled)
skeleton := hdlskeleton.NewSkeleton()

skeleton.AddPage(hdlskeleton.Title{Title: "Info", Style: hdlskeleton.TitleStyle{
Active: ts.TitleStyleActive,
Inactive: ts.TitleStyleInactive,
}}, hdlinfo.SetupModelInfo(githubUseCase, version))

skeleton.AddPage(hdlskeleton.Title{Title: "Repository", Style: hdlskeleton.TitleStyle{
Active: ts.TitleStyleActive,
Inactive: ts.TitleStyleInactive,
}}, hdlgithubrepo.SetupModelGithubRepository(githubUseCase))

skeleton.AddPage(hdlskeleton.Title{Title: "Workflow History", Style: hdlskeleton.TitleStyle{
Active: ts.TitleStyleActive,
Inactive: ts.TitleStyleInactive,
}}, hdlworkflowhistory.SetupModelGithubWorkflowHistory(githubUseCase))

skeleton.AddPage(hdlskeleton.Title{Title: "Workflow", Style: hdlskeleton.TitleStyle{
Active: ts.TitleStyleActive,
Inactive: ts.TitleStyleInactive,
}}, hdlWorkflow.SetupModelGithubWorkflow(githubUseCase))

skeleton.AddPage(hdlskeleton.Title{Title: "Trigger", Style: hdlskeleton.TitleStyle{
Active: ts.TitleStyleActive,
Inactive: ts.TitleStyleInactive,
}}, hdltrigger.SetupModelGithubTrigger(githubUseCase))

m := model{
viewport: hdltypes.NewTerminalViewport(),
modelError: &hdlModelError,
modelHeader: hdlModelHeader,
modelInfo: hdlModelInfo,
modelGithubRepository: hdlModelGithubRepository,
modelWorkflowHistory: hdlModelWorkflowHistory,
modelWorkflow: hdlModelWorkflow,
modelTrigger: hdlModelTrigger,
keys: keys,
viewport: hdltypes.NewTerminalViewport(),
modelSkeleton: skeleton,
keys: keys,
}

return &m
@@ -73,42 +66,16 @@ func (m *model) Init() tea.Cmd {
return tea.Batch(
tea.EnterAltScreen,
tea.SetWindowTitle("GitHub Actions Manager (GAMA)"),
m.modelHeader.Init(),
m.modelInfo.Init(),
m.modelGithubRepository.Init(),
m.modelWorkflowHistory.Init(),
m.modelWorkflow.Init(),
m.modelTrigger.Init())
m.modelSkeleton.Init(),
)
}

func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd

switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.viewport.Width = msg.Width
m.viewport.Height = msg.Height
case tea.KeyMsg:
// Handle global keybindings
switch {
case key.Matches(msg, m.keys.Quit):
return m, tea.Quit
}

m.modelHeader, cmd = m.modelHeader.Update(msg)
cmds = append(cmds, cmd)
cmds = append(cmds, m.handleTabContent(cmd, msg))
case hdlinfo.UpdateSpinnerMsg:
m.modelInfo, cmd = m.modelInfo.Update(msg)
cmds = append(cmds, cmd)
case header.UpdateMsg:
m.modelHeader, cmd = m.modelHeader.Update(msg)
cmds = append(cmds, cmd)
case hdlworkflowhistory.UpdateWorkflowHistoryMsg:
m.modelWorkflowHistory, cmd = m.modelWorkflowHistory.Update(msg)
cmds = append(cmds, cmd)
}
var cmd tea.Cmd
m.modelSkeleton, cmd = m.modelSkeleton.Update(msg)
cmds = append(cmds, cmd)

return m, tea.Batch(cmds...)
}
@@ -118,58 +85,5 @@ func (m *model) View() string {
return fmt.Sprintf("Terminal window is too small. Please resize to at least %dx%d.", hdltypes.MinTerminalWidth, hdltypes.MinTerminalHeight)
}

var mainDoc strings.Builder
var helpDoc string
var operationDoc string

var width = lipgloss.Width(strings.Repeat("-", m.viewport.Width)) - 5
hdltypes.ScreenWidth = &width

dynamicWindowStyle := lipgloss.NewStyle().Width(width).Height(m.viewport.Height - 22)
helpWindowStyle := ts.WindowStyleHelp.Width(width)

mainDoc.WriteString(m.modelHeader.View() + "\n")
switch m.modelHeader.GetCurrentTab() {
case 0:
mainDoc.WriteString(dynamicWindowStyle.Render(m.modelInfo.View()))
operationDoc = m.modelInfo.ViewStatus()
helpDoc = helpWindowStyle.Render(m.modelInfo.ViewHelp())
case 1:
mainDoc.WriteString(dynamicWindowStyle.Render(m.modelGithubRepository.View()))
operationDoc = m.modelGithubRepository.ViewStatus()
helpDoc = helpWindowStyle.Render(m.modelGithubRepository.ViewHelp())
case 2:
mainDoc.WriteString(dynamicWindowStyle.Render(m.modelWorkflowHistory.View()))
operationDoc = m.modelWorkflowHistory.ViewStatus()
helpDoc = helpWindowStyle.Render(m.modelWorkflowHistory.ViewHelp())
case 3:
mainDoc.WriteString(dynamicWindowStyle.Render(m.modelWorkflow.View()))
operationDoc = m.modelWorkflow.ViewStatus()
helpDoc = helpWindowStyle.Render(m.modelWorkflow.ViewHelp())
case 4:
mainDoc.WriteString(dynamicWindowStyle.Render(m.modelTrigger.View()))
operationDoc = m.modelTrigger.ViewStatus()
helpDoc = helpWindowStyle.Render(m.modelTrigger.ViewHelp())
}

mainDocContent := ts.DocStyle.Render(mainDoc.String())
informationPane := lipgloss.JoinVertical(lipgloss.Top, operationDoc, helpDoc)

return lipgloss.JoinVertical(lipgloss.Top, mainDocContent, informationPane)
}

func (m *model) handleTabContent(cmd tea.Cmd, msg tea.Msg) tea.Cmd {
switch m.modelHeader.GetCurrentTab() {
case 0:
m.modelInfo, cmd = m.modelInfo.Update(msg)
case 1:
m.modelGithubRepository, cmd = m.modelGithubRepository.Update(msg)
case 2:
m.modelWorkflowHistory, cmd = m.modelWorkflowHistory.Update(msg)
case 3:
m.modelWorkflow, cmd = m.modelWorkflow.Update(msg)
case 4:
m.modelTrigger, cmd = m.modelTrigger.Update(msg)
}
return cmd
return m.modelSkeleton.View()
}
32 changes: 12 additions & 20 deletions internal/terminal/handler/header/header.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/termkit/gama/internal/terminal/handler/spirit"
"github.com/termkit/gama/internal/terminal/handler/types"
ts "github.com/termkit/gama/internal/terminal/style"
"strings"
@@ -19,7 +20,8 @@ type Header struct {
keys keyMap

currentTab int
lockTabs bool

modelSpirit *spirit.ModelSpirit

commonHeaders []commonHeader
specialHeader specialHeader
@@ -51,11 +53,13 @@ var (
// NewHeader returns a new Header.
func NewHeader() *Header {
once.Do(func() {
s := spirit.NewSpirit()
s.SetLockTabs(false)
h = &Header{
modelSpirit: s,
specialHeaderInterval: time.Millisecond * 100,
Viewport: types.NewTerminalViewport(),
currentTab: 0,
lockTabs: true,
keys: keys,
}
})
@@ -70,15 +74,7 @@ func (h *Header) GetCurrentTab() int {
return h.currentTab
}

func (h *Header) SetLockTabs(lock bool) {
h.lockTabs = lock
}

func (h *Header) GetLockTabs() bool {
return h.lockTabs
}

func (h *Header) AddCommonHeader(header string, inactiveStyle, activeStyle lipgloss.Style) {
func (h *Header) AddCommonHeader(header string, activeStyle, inactiveStyle lipgloss.Style) {
h.commonHeaders = append(h.commonHeaders, commonHeader{
header: header,
rawHeader: header,
@@ -126,11 +122,11 @@ func (h *Header) Update(msg tea.Msg) (*Header, tea.Cmd) {
case tea.KeyMsg:
switch {
case key.Matches(msg, h.keys.SwitchTabLeft):
if !h.lockTabs {
if !h.modelSpirit.GetLockTabs() {
h.currentTab = max(h.currentTab-1, 0)
}
case key.Matches(msg, h.keys.SwitchTabRight):
if !h.lockTabs {
if !h.modelSpirit.GetLockTabs() {
h.currentTab = min(h.currentTab+1, len(h.commonHeaders)-1)
}
}
@@ -158,11 +154,10 @@ func (h *Header) View() string {
titles += "LLL RRR"
}
titleLen := len(titles)
specialTitleLen := len(h.specialHeader.header)

var renderedTitles []string
for i, title := range h.commonHeaders {
if h.lockTabs {
if h.modelSpirit.GetLockTabs() {
if i == 0 {
renderedTitles = append(renderedTitles, title.activeStyle.Render(title.header))
} else {
@@ -177,10 +172,7 @@ func (h *Header) View() string {
}
}

var specialHeader string
specialHeader = h.specialHeader.styles[h.currentSpecialStyle].Render(h.specialHeader.header)

line := strings.Repeat("─", h.Viewport.Width-(titleLen+specialTitleLen))
line := strings.Repeat("─", h.Viewport.Width-(titleLen)+10)

return lipgloss.JoinHorizontal(lipgloss.Center, append(renderedTitles, line, specialHeader)...)
return lipgloss.JoinHorizontal(lipgloss.Center, append(renderedTitles, line)...)
}
24 changes: 14 additions & 10 deletions internal/terminal/handler/information/information.go
Original file line number Diff line number Diff line change
@@ -10,8 +10,9 @@ import (
"github.com/charmbracelet/lipgloss"
gu "github.com/termkit/gama/internal/github/usecase"
hdlerror "github.com/termkit/gama/internal/terminal/handler/error"
"github.com/termkit/gama/internal/terminal/handler/header"
"github.com/termkit/gama/internal/terminal/handler/spirit"
"github.com/termkit/gama/internal/terminal/handler/types"
ts "github.com/termkit/gama/internal/terminal/style"
pkgversion "github.com/termkit/gama/pkg/version"
"strings"
"time"
@@ -26,7 +27,7 @@ type ModelInfo struct {
complete bool

// models
modelHeader *header.Header
modelSpirit *spirit.ModelSpirit

Help help.Model
Viewport *viewport.Model
@@ -59,15 +60,15 @@ var (

func SetupModelInfo(githubUseCase gu.UseCase, version pkgversion.Version) *ModelInfo {
modelError := hdlerror.SetupModelError()
hdlModelHeader := header.NewHeader()
//hdlModelHeader := header.NewHeader()

s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("120"))

return &ModelInfo{
Viewport: types.NewTerminalViewport(),
modelHeader: hdlModelHeader,
modelSpirit: spirit.NewSpirit(),
github: githubUseCase,
version: version,
Help: help.New(),
@@ -114,7 +115,7 @@ func (m *ModelInfo) checkUpdates(ctx context.Context) {
go m.Update(m)
}

func (m *ModelInfo) Update(msg tea.Msg) (*ModelInfo, tea.Cmd) {
func (m *ModelInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg.(type) {
case UpdateSpinnerMsg:
@@ -137,30 +138,33 @@ func (m *ModelInfo) View() string {
BorderForeground(lipgloss.Color("39")).
Align(lipgloss.Center).
Border(lipgloss.RoundedBorder()).
Width(m.Viewport.Width - 7)
Width(m.Viewport.Width - 3)

helpWindowStyle := ts.WindowStyleHelp.Width(m.Viewport.Width - 4)

infoDoc.WriteString(lipgloss.JoinVertical(lipgloss.Center, applicationName, applicationDescription, newVersionAvailableMsg))

docHeight := lipgloss.Height(infoDoc.String())
requiredNewlinesForPadding := m.Viewport.Height - docHeight - 12
requiredNewlinesForPadding := m.Viewport.Height - docHeight - 11

infoDoc.WriteString(strings.Repeat("\n", max(0, requiredNewlinesForPadding)))

return ws.Render(infoDoc.String())
return lipgloss.JoinVertical(lipgloss.Top, ws.Render(infoDoc.String()), m.modelError.View(), helpWindowStyle.Render(m.ViewHelp()))
}

func (m *ModelInfo) testConnection(ctx context.Context) {
//time.Sleep(3 * time.Second)
_, err := m.github.GetAuthUser(ctx)
if err != nil {
m.modelError.SetError(err)
m.modelError.SetErrorMessage("failed to test connection, please check your token&permission")
m.modelHeader.SetLockTabs(true)
m.modelSpirit.SetLockTabs(true)
return
}

m.modelError.Reset()
m.modelError.SetSuccessMessage("Welcome to GAMA!")
m.modelHeader.SetLockTabs(false)
m.modelSpirit.SetLockTabs(false)
m.complete = true
}

32 changes: 32 additions & 0 deletions internal/terminal/handler/skeleton/keymap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package skeleton

import (
"fmt"
"github.com/termkit/gama/internal/config"

teakey "github.com/charmbracelet/bubbles/key"
)

type keyMap struct {
SwitchTabRight teakey.Binding
SwitchTabLeft teakey.Binding
Quit teakey.Binding
}

var keys = func() keyMap {
cfg, err := config.LoadConfig()
if err != nil {
panic(fmt.Sprintf("failed to load config: %v", err))
}
return keyMap{
SwitchTabRight: teakey.NewBinding(
teakey.WithKeys(cfg.Shortcuts.SwitchTabRight),
),
SwitchTabLeft: teakey.NewBinding(
teakey.WithKeys(cfg.Shortcuts.SwitchTabLeft),
),
Quit: teakey.NewBinding(
teakey.WithKeys(cfg.Shortcuts.Quit),
),
}
}()
120 changes: 120 additions & 0 deletions internal/terminal/handler/skeleton/skeleton.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package skeleton

import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/termkit/gama/internal/terminal/handler/header"
"github.com/termkit/gama/internal/terminal/handler/spirit"
"github.com/termkit/gama/internal/terminal/handler/types"
"sync"
)

// Skeleton is a helper for rendering the Skeleton of the terminal.
type Skeleton struct {
Viewport *viewport.Model

header *header.Header
modelSpirit *spirit.ModelSpirit
lockTabs bool

keys keyMap

currentTab int
Pages []tea.Model
}

func (s *Skeleton) AddPage(title Title, page tea.Model) {
s.header.AddCommonHeader(title.Title, title.Style.Active, title.Style.Inactive)
s.Pages = append(s.Pages, page)
}

type Title struct {
Title string
Style TitleStyle
}

type TitleStyle struct {
Active lipgloss.Style
Inactive lipgloss.Style
}

var (
once sync.Once
s *Skeleton
)

// NewSkeleton returns a new Skeleton.
func NewSkeleton() *Skeleton {
once.Do(func() {
s = &Skeleton{
Viewport: types.NewTerminalViewport(),
header: header.NewHeader(),
modelSpirit: spirit.NewSpirit(),
keys: keys,
}
})
return s
}

type SwitchTab struct {
Tab int
}

func (s *Skeleton) SetCurrentTab(tab int) {
s.currentTab = tab
}

func (s *Skeleton) Init() tea.Cmd {
self := func() tea.Msg {
return SwitchTab{}
}

inits := make([]tea.Cmd, len(s.Pages)+1) // +1 for self
for i := range s.Pages {
inits[i] = s.Pages[i].Init()
}

inits[len(s.Pages)] = self

return tea.Batch(inits...)
}

func (s *Skeleton) Update(msg tea.Msg) (*Skeleton, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
s.Viewport.Width = msg.Width
s.Viewport.Height = msg.Height
//case SwitchTab:
// s.SetCurrentTab(msg.Tab)
// s.header.SetCurrentTab(msg.Tab)
//
// var cmd tea.Cmd
// s.Pages[s.currentTab], cmd = s.Pages[msg.Tab].Update(msg)
// return s, cmd
case tea.KeyMsg:
switch {
case key.Matches(msg, s.keys.Quit):
return s, tea.Quit
case key.Matches(msg, s.keys.SwitchTabLeft):
if !s.modelSpirit.GetLockTabs() {
s.currentTab = max(s.currentTab-1, 0)
}
case key.Matches(msg, s.keys.SwitchTabRight):
if !s.modelSpirit.GetLockTabs() {
s.currentTab = min(s.currentTab+1, len(s.Pages)-1)
}
}
}

var cmd tea.Cmd
s.header, cmd = s.header.Update(msg)
s.Pages[s.currentTab], cmd = s.Pages[s.currentTab].Update(msg)

return s, cmd
}

func (s *Skeleton) View() string {
return lipgloss.JoinVertical(lipgloss.Top, s.header.View(), s.Pages[s.currentTab].View())
}
28 changes: 28 additions & 0 deletions internal/terminal/handler/spirit/spirit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package spirit

import "sync"

type ModelSpirit struct {
lockTabs bool
}

var (
once sync.Once
s *ModelSpirit
)

// NewSpirit returns a new Spirit.
func NewSpirit() *ModelSpirit {
once.Do(func() {
s = &ModelSpirit{}
})
return s
}

func (s *ModelSpirit) SetLockTabs(lock bool) {
s.lockTabs = lock
}

func (s *ModelSpirit) GetLockTabs() bool {
return s.lockTabs
}
1 change: 1 addition & 0 deletions internal/terminal/handler/taboptions/taboptions.go
Original file line number Diff line number Diff line change
@@ -114,6 +114,7 @@ func (o *Options) View() string {
var style = o.Style.Foreground(lipgloss.Color("15"))

var opts []string
opts = append(opts, " ")

for i, option := range o.optionsAction {
switch o.status {
2 changes: 0 additions & 2 deletions internal/terminal/handler/types/types.go
Original file line number Diff line number Diff line change
@@ -24,8 +24,6 @@ func NewSelectedRepository() *SelectedRepository {
return selectedRepository
}

var ScreenWidth *int

// ----------------------------------------------

const (