diff --git a/internal/ui/app/launch.go b/internal/app/launch.go similarity index 93% rename from internal/ui/app/launch.go rename to internal/app/launch.go index b4eb3ad..cf9a8e2 100644 --- a/internal/ui/app/launch.go +++ b/internal/app/launch.go @@ -2,7 +2,7 @@ package app import ( tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/ui/logger" + "github.com/michaelhass/gitglance/internal/core/logger" ) type Option func(opts *options) diff --git a/internal/ui/app/model.go b/internal/app/model.go similarity index 88% rename from internal/ui/app/model.go rename to internal/app/model.go index 1df9478..4d5d533 100644 --- a/internal/ui/app/model.go +++ b/internal/app/model.go @@ -6,11 +6,11 @@ import ( "time" tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/ui/dialog" - "github.com/michaelhass/gitglance/internal/ui/exit" - "github.com/michaelhass/gitglance/internal/ui/logger" - "github.com/michaelhass/gitglance/internal/ui/refresh" - "github.com/michaelhass/gitglance/internal/ui/status" + "github.com/michaelhass/gitglance/internal/core/exit" + "github.com/michaelhass/gitglance/internal/core/logger" + "github.com/michaelhass/gitglance/internal/core/refresh" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" + "github.com/michaelhass/gitglance/internal/page/status" ) // refreshInterval is the duration when a refresh.Msg is send. diff --git a/internal/editor/editor.go b/internal/core/editor/editor.go similarity index 100% rename from internal/editor/editor.go rename to internal/core/editor/editor.go diff --git a/internal/editor/editor_test.go b/internal/core/editor/editor_test.go similarity index 100% rename from internal/editor/editor_test.go rename to internal/core/editor/editor_test.go diff --git a/internal/ui/exit/cmd.go b/internal/core/exit/cmd.go similarity index 100% rename from internal/ui/exit/cmd.go rename to internal/core/exit/cmd.go diff --git a/internal/git/cmd.go b/internal/core/git/cmd.go similarity index 97% rename from internal/git/cmd.go rename to internal/core/git/cmd.go index 1f1d1c9..323a7d1 100644 --- a/internal/git/cmd.go +++ b/internal/core/git/cmd.go @@ -94,7 +94,7 @@ func newDiffCmd(opts DiffOptions) *gitCommand { } if opts.IsUntracked { - args = append(args, untrackedFileDiffArgs[:3]...) + args = append(args, untrackedFileDiffArgs[:]...) } if len(opts.FilePath) > 0 { diff --git a/internal/git/git.go b/internal/core/git/git.go similarity index 77% rename from internal/git/git.go rename to internal/core/git/git.go index 89a147e..0b4b0eb 100644 --- a/internal/git/git.go +++ b/internal/core/git/git.go @@ -109,3 +109,36 @@ func IsInWorkTree() bool { func StashAll() error { return newGitCommand("stash", "-u").run() } + +func GetStash() (Stash, error) { + out, err := newGitCommand("stash", "list").output() + if err != nil { + return Stash([]StashEntry{}), err + } + return newDefaultStashBuilder(). + makeStashFromMultilineText(out) +} + +func ApplyStashEntry(entry StashEntry) error { + return ApplyStashIndex(entry.Index()) +} + +func ApplyStashIndex(index int) error { + return newGitCommand("stash", "apply", fmt.Sprintf("%d", index)).run() +} + +func PopStashEntry(entry StashEntry) error { + return PopStashIndex(entry.Index()) +} + +func PopStashIndex(index int) error { + return newGitCommand("stash", "pop", fmt.Sprintf("%d", index)).run() +} + +func DropStashEntry(entry StashEntry) error { + return DropStashIndex(entry.Index()) +} + +func DropStashIndex(index int) error { + return newGitCommand("stash", "drop", fmt.Sprintf("%d", index)).run() +} diff --git a/internal/core/git/stash.go b/internal/core/git/stash.go new file mode 100644 index 0000000..ec7d175 --- /dev/null +++ b/internal/core/git/stash.go @@ -0,0 +1,94 @@ +package git + +import ( + "errors" + "regexp" + "strconv" + "strings" +) + +const ( + stashEntryIdxRegexPattern = `stash@{([0-9]+)}` + stashEntryComponentSeparator = ": " +) + +var ( + missingStashIdxErr = errors.New("No stash index found") + missingStashMsgErr = errors.New("No stash message found") + defaultStashIdxRegex *regexp.Regexp = regexp.MustCompile(stashEntryIdxRegexPattern) +) + +type stashBuilder struct { + stashEntryIdxRegex *regexp.Regexp +} + +func newDefaultStashBuilder() stashBuilder { + return stashBuilder{ + stashEntryIdxRegex: defaultStashIdxRegex, + } +} + +func (b stashBuilder) makeStashFromMultilineText(text string) (Stash, error) { + lines := strings.Split(text, "\n") + return b.makeStashFromLines(lines...) +} + +func (b stashBuilder) makeStashFromLines(lines ...string) (Stash, error) { + var ( + entries []StashEntry + err error + ) + + for _, line := range lines { + idx, idxErr := b.getStashEntryIdxFromLine(line) + if idxErr != nil { + err = idxErr + break + } + msg, msgErr := b.getStashEntryMsgFromLine(line) + if msgErr != nil { + err = msgErr + break + } + + entries = append(entries, StashEntry{idx: idx, msg: msg}) + } + + return entries, err +} + +func (b stashBuilder) getStashEntryIdxFromLine(line string) (int, error) { + matches := b.stashEntryIdxRegex.FindStringSubmatch(line) + if len(matches) < 2 { + return -1, missingStashIdxErr + } + idx, err := strconv.Atoi(matches[1]) + if err != nil { + return -1, missingStashIdxErr + } + return idx, nil +} + +func (b stashBuilder) getStashEntryMsgFromLine(line string) (string, error) { + idx := strings.Index(line, stashEntryComponentSeparator) + if idx == -1 { + return "", missingStashMsgErr + } + msgStartIdx := idx + len(stashEntryComponentSeparator) + return line[msgStartIdx:], nil +} + +type StashEntry struct { + idx int + msg string +} + +func (s StashEntry) Message() string { + return s.msg +} + +func (s StashEntry) Index() int { + return s.idx +} + +type Stash []StashEntry diff --git a/internal/core/git/stash_test.go b/internal/core/git/stash_test.go new file mode 100644 index 0000000..2237fb4 --- /dev/null +++ b/internal/core/git/stash_test.go @@ -0,0 +1,63 @@ +package git + +import ( + "fmt" + "testing" +) + +func TestGetStashIdx(t *testing.T) { + tests := []struct { + input string + expectIdx int + hasError bool + }{ + {"stash@{12}", 12, false}, + {"prefix stash@{12}", 12, false}, + {"stash@{12} stash@{3} multiple", 12, false}, + {"stash@{} empty", -1, true}, + {"25", -1, true}, + } + + builder := newDefaultStashBuilder() + for _, tt := range tests { + testName := fmt.Sprintf("Reading idx from: `%s`", tt.input) + t.Run(testName, func(t *testing.T) { + gotIdx, gotErr := builder.getStashEntryIdxFromLine(tt.input) + if (tt.hasError && gotErr == nil) || + (!tt.hasError && gotErr != nil) { + t.Error("Unexpected error result:", gotErr) + } + if gotIdx != tt.expectIdx { + t.Errorf("Got idx `%d` but expected `%d.`", gotIdx, tt.expectIdx) + } + }) + } +} + +func TestGetStashEntryMsg(t *testing.T) { + tests := []struct { + input string + expectMsg string + hasError bool + }{ + {"stash@{12}", "", true}, + {": ", "", false}, + {"stash@{12}: this is the message", "this is the message", false}, + {"stash@{12}: this is the message: more message", "this is the message: more message", false}, + } + + builder := newDefaultStashBuilder() + for _, tt := range tests { + testName := fmt.Sprintf("Reading message from: `%s`", tt.input) + t.Run(testName, func(t *testing.T) { + gotMsg, gotErr := builder.getStashEntryMsgFromLine(tt.input) + if (tt.hasError && gotErr == nil) || + (!tt.hasError && gotErr != nil) { + t.Error("Unexpected error result:", gotErr) + } + if gotMsg != tt.expectMsg { + t.Errorf("Got msg`%s` but expected `%s`.", gotMsg, tt.expectMsg) + } + }) + } +} diff --git a/internal/git/status.go b/internal/core/git/status.go similarity index 95% rename from internal/git/status.go rename to internal/core/git/status.go index 5d9a0f5..40f46e7 100644 --- a/internal/git/status.go +++ b/internal/core/git/status.go @@ -110,15 +110,8 @@ func readFileStatusFromOutputComponent(component string) (FileStatus, error) { } func cleanedPathString(path string) string { - path = strings.TrimSuffix(path, " ") - path = strings.TrimPrefix(path, " ") - - if strings.Contains(path, " ") && - strings.HasPrefix(path, "\"") && - strings.HasSuffix(path, "\"") { - path = path[1 : len(path)-1] - } - + path = strings.Trim(path, " ") + path = strings.Trim(path, "\"") return path } diff --git a/internal/git/status_test.go b/internal/core/git/status_test.go similarity index 100% rename from internal/git/status_test.go rename to internal/core/git/status_test.go diff --git a/internal/ui/logger/logger.go b/internal/core/logger/logger.go similarity index 100% rename from internal/ui/logger/logger.go rename to internal/core/logger/logger.go diff --git a/internal/ui/refresh/refresh.go b/internal/core/refresh/refresh.go similarity index 100% rename from internal/ui/refresh/refresh.go rename to internal/core/refresh/refresh.go diff --git a/internal/textwrap/builder.go b/internal/core/textwrap/builder.go similarity index 100% rename from internal/textwrap/builder.go rename to internal/core/textwrap/builder.go diff --git a/internal/textwrap/builder_test.go b/internal/core/textwrap/builder_test.go similarity index 100% rename from internal/textwrap/builder_test.go rename to internal/core/textwrap/builder_test.go diff --git a/internal/textwrap/characterwrap.go b/internal/core/textwrap/characterwrap.go similarity index 100% rename from internal/textwrap/characterwrap.go rename to internal/core/textwrap/characterwrap.go diff --git a/internal/textwrap/characterwrap_test.go b/internal/core/textwrap/characterwrap_test.go similarity index 100% rename from internal/textwrap/characterwrap_test.go rename to internal/core/textwrap/characterwrap_test.go diff --git a/internal/textwrap/renderer.go b/internal/core/textwrap/renderer.go similarity index 100% rename from internal/textwrap/renderer.go rename to internal/core/textwrap/renderer.go diff --git a/internal/textwrap/wordwrap.go b/internal/core/textwrap/wordwrap.go similarity index 100% rename from internal/textwrap/wordwrap.go rename to internal/core/textwrap/wordwrap.go diff --git a/internal/textwrap/wordwrap_test.go b/internal/core/textwrap/wordwrap_test.go similarity index 100% rename from internal/textwrap/wordwrap_test.go rename to internal/core/textwrap/wordwrap_test.go diff --git a/internal/textwrap/wrapper.go b/internal/core/textwrap/wrapper.go similarity index 100% rename from internal/textwrap/wrapper.go rename to internal/core/textwrap/wrapper.go diff --git a/internal/ui/container/container.go b/internal/core/ui/components/container/container.go similarity index 86% rename from internal/ui/container/container.go rename to internal/core/ui/components/container/container.go index b3fd9bc..7b3d81d 100644 --- a/internal/ui/container/container.go +++ b/internal/core/ui/components/container/container.go @@ -6,7 +6,7 @@ package container import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - styles "github.com/michaelhass/gitglance/internal/ui/style" + "github.com/michaelhass/gitglance/internal/core/ui/style" ) const ( @@ -17,11 +17,11 @@ const ( ) var ( - inactiveTitleStyle = styles.InactiveTitle.Height(titleHeight) - focusTitleStyle = styles.Title.Height(titleHeight) + inactiveTitleStyle = style.InactiveTitle.Height(titleHeight) + focusTitleStyle = style.Title.Height(titleHeight) - inactiveBorderStyle = styles.InactiveBorder.PaddingLeft(paddingLeft) - focusBorderStyle = styles.FocusBorder.PaddingLeft(paddingLeft) + inactiveBorderStyle = style.InactiveBorder.PaddingLeft(paddingLeft) + focusBorderStyle = style.FocusBorder.PaddingLeft(paddingLeft) ) type Model struct { diff --git a/internal/ui/container/content.go b/internal/core/ui/components/container/content.go similarity index 100% rename from internal/ui/container/content.go rename to internal/core/ui/components/container/content.go diff --git a/internal/ui/dialog/cmd.go b/internal/core/ui/components/dialog/cmd.go similarity index 100% rename from internal/ui/dialog/cmd.go rename to internal/core/ui/components/dialog/cmd.go diff --git a/internal/ui/confirm/confirm.go b/internal/core/ui/components/dialog/confirm/confirm.go similarity index 85% rename from internal/ui/confirm/confirm.go rename to internal/core/ui/components/dialog/confirm/confirm.go index 872ee19..0e9c65c 100644 --- a/internal/ui/confirm/confirm.go +++ b/internal/core/ui/components/dialog/confirm/confirm.go @@ -4,8 +4,8 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/michaelhass/gitglance/internal/ui/dialog" - styles "github.com/michaelhass/gitglance/internal/ui/style" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" + styles "github.com/michaelhass/gitglance/internal/core/ui/style" ) const ( @@ -43,7 +43,7 @@ func (m Model) Init() tea.Cmd { return nil } -func (m Model) Update(msg tea.Msg) (dialog.Content, tea.Cmd) { +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok && key.Matches(keyMsg, m.keys.confirm) { return m, tea.Sequence(m.confirmCmd, dialog.Close()) } @@ -67,7 +67,7 @@ func (m Model) Help() []key.Binding { } } -func (m Model) SetSize(width, height int) dialog.Content { +func (m Model) SetSize(width, height int) Model { m.width = width m.height = height return m diff --git a/internal/core/ui/components/dialog/confirm/dialog.go b/internal/core/ui/components/dialog/confirm/dialog.go new file mode 100644 index 0000000..359ab1b --- /dev/null +++ b/internal/core/ui/components/dialog/confirm/dialog.go @@ -0,0 +1,33 @@ +package confirm + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" +) + +type DialogContent struct { + Model +} + +func NewDialogConent(confirm Model) DialogContent { + return DialogContent{Model: confirm} +} + +func (dc DialogContent) Init() tea.Cmd { + return dc.Model.Init() +} + +func (dc DialogContent) Update(msg tea.Msg) (dialog.Content, tea.Cmd) { + model, cmd := dc.Model.Update(msg) + dc.Model = model + return dc, cmd +} + +func (dc DialogContent) View() string { + return dc.Model.View() +} + +func (dc DialogContent) SetSize(width, height int) dialog.Content { + dc.Model = dc.Model.SetSize(width, height) + return dc +} diff --git a/internal/ui/confirm/key.go b/internal/core/ui/components/dialog/confirm/key.go similarity index 100% rename from internal/ui/confirm/key.go rename to internal/core/ui/components/dialog/confirm/key.go diff --git a/internal/ui/dialog/content.go b/internal/core/ui/components/dialog/content.go similarity index 100% rename from internal/ui/dialog/content.go rename to internal/core/ui/components/dialog/content.go diff --git a/internal/ui/dialog/dialog.go b/internal/core/ui/components/dialog/dialog.go similarity index 97% rename from internal/ui/dialog/dialog.go rename to internal/core/ui/components/dialog/dialog.go index 9ae028f..10ec61f 100644 --- a/internal/ui/dialog/dialog.go +++ b/internal/core/ui/components/dialog/dialog.go @@ -5,7 +5,7 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/michaelhass/gitglance/internal/ui/style" + "github.com/michaelhass/gitglance/internal/core/ui/style" ) type DisplayMode byte diff --git a/internal/core/ui/components/list/container.go b/internal/core/ui/components/list/container.go new file mode 100644 index 0000000..32ad432 --- /dev/null +++ b/internal/core/ui/components/list/container.go @@ -0,0 +1,37 @@ +package list + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/ui/components/container" +) + +// ContainerContent is a wrapper to use the filelist ui as container.ContainerContent. +type ContainerContent struct { + Model +} + +func NewContainerContent(model Model) ContainerContent { + return ContainerContent{Model: model} +} + +func NewContainer(model Model) container.Model { + containerContent := NewContainerContent(model) + return container.New(containerContent) +} + +func (c ContainerContent) Update(msg tea.Msg) (container.Content, tea.Cmd) { + model, cmd := c.Model.Update(msg) + c.Model = model + return c, cmd +} + +func (c ContainerContent) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) { + model, cmd := c.Model.UpdateFocus(isFocused) + c.Model = model + return c, cmd +} + +func (c ContainerContent) SetSize(width, height int) container.Content { + c.Model = c.Model.SetSize(width, height) + return c +} diff --git a/internal/ui/list/file/item.go b/internal/core/ui/components/list/file/item.go similarity index 91% rename from internal/ui/list/file/item.go rename to internal/core/ui/components/list/file/item.go index bf5b004..d0c76c3 100644 --- a/internal/ui/list/file/item.go +++ b/internal/core/ui/components/list/file/item.go @@ -3,7 +3,7 @@ package file import ( "fmt" - "github.com/michaelhass/gitglance/internal/git" + "github.com/michaelhass/gitglance/internal/core/git" ) type Item struct { diff --git a/internal/ui/list/item.go b/internal/core/ui/components/list/item.go similarity index 100% rename from internal/ui/list/item.go rename to internal/core/ui/components/list/item.go diff --git a/internal/ui/list/key.go b/internal/core/ui/components/list/key.go similarity index 100% rename from internal/ui/list/key.go rename to internal/core/ui/components/list/key.go diff --git a/internal/ui/list/list.go b/internal/core/ui/components/list/list.go similarity index 98% rename from internal/ui/list/list.go rename to internal/core/ui/components/list/list.go index 050522d..8f7336a 100644 --- a/internal/ui/list/list.go +++ b/internal/core/ui/components/list/list.go @@ -7,7 +7,7 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/michaelhass/gitglance/internal/ui/style" + "github.com/michaelhass/gitglance/internal/core/ui/style" ) var ( diff --git a/internal/ui/list/msg.go b/internal/core/ui/components/list/msg.go similarity index 100% rename from internal/ui/list/msg.go rename to internal/core/ui/components/list/msg.go diff --git a/internal/core/ui/components/textinput/container.go b/internal/core/ui/components/textinput/container.go new file mode 100644 index 0000000..7771c4d --- /dev/null +++ b/internal/core/ui/components/textinput/container.go @@ -0,0 +1,36 @@ +package textinput + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/ui/components/container" +) + +type ContainerContent struct { + Model +} + +func NewContainerContent(model Model) ContainerContent { + return ContainerContent{Model: model} +} + +func NewContainer(model Model) container.Model { + containerContent := NewContainerContent(model) + return container.New(containerContent) +} + +func (c ContainerContent) Update(msg tea.Msg) (container.Content, tea.Cmd) { + model, cmd := c.Model.Update(msg) + c.Model = model + return c, cmd +} + +func (c ContainerContent) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) { + model, cmd := c.Model.UpdateFocus(isFocused) + c.Model = model + return c, cmd +} + +func (c ContainerContent) SetSize(width, height int) container.Content { + c.Model = c.Model.SetSize(width, height) + return c +} diff --git a/internal/core/ui/components/textinput/textinput.go b/internal/core/ui/components/textinput/textinput.go new file mode 100644 index 0000000..c976ce5 --- /dev/null +++ b/internal/core/ui/components/textinput/textinput.go @@ -0,0 +1,132 @@ +package textinput + +import ( + "fmt" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/textarea" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/michaelhass/gitglance/internal/core/ui/style" +) + +var ( + countStyle = style.SublteText.Height(1) +) + +type Model struct { + title string + + textarea textarea.Model + + width int + height int + + isFocused bool +} + +func New(title string, placeholder string) Model { + var ( + textarea = textarea.New() + blurredStyle = textarea.BlurredStyle + focusedStyle = textarea.FocusedStyle + ) + + textarea.Placeholder = placeholder + textarea.Prompt = "" + textarea.ShowLineNumbers = false + + blurredStyle.Text = style.SublteText + blurredStyle.Placeholder = style.SublteText + blurredStyle.CursorLine = lipgloss.NewStyle() + textarea.BlurredStyle = blurredStyle + + focusedStyle.Placeholder = style.SublteText + focusedStyle.Text = style.Text + focusedStyle.CursorLine = lipgloss.NewStyle() + textarea.FocusedStyle = focusedStyle + + return Model{ + title: title, + textarea: textarea, + } +} + +func (m Model) Init() tea.Cmd { + return nil +} + +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + textarea, cmd := m.textarea.Update(msg) + m.textarea = textarea + return m, cmd +} + +func (m Model) UpdateFocus(isFocused bool) (Model, tea.Cmd) { + m.isFocused = isFocused + if isFocused { + m.textarea.Focus() + } else { + m.textarea.Blur() + } + return m, nil +} + +func (m Model) View() string { + inputLength := len([]rune(m.textarea.Value())) + + var countLine string + if inputLength > 0 { + count := countStyle. + MaxWidth(m.width - 2). + Render(fmt.Sprintf("[%d chars]", inputLength)) + + countLine = lipgloss.PlaceHorizontal( + m.width-2, + lipgloss.Right, + count, + ) + } + + return lipgloss.JoinVertical( + lipgloss.Top, + m.textarea.View(), + countLine, + ) +} + +func (m Model) Title() string { + return m.title +} + +func (m Model) SetValue(value string) Model { + m.textarea.SetValue(value) + return m +} + +func (m Model) SetCursorToStart() Model { + // textarea.Line() does not seem to return the correct current + // line of the cursor. At least after setting a new value. + // Thus, move the cursor up more than potentially needed to ensure + // that we are at the very beginning of the text input. + for i := 0; i < m.textarea.LineCount(); i++ { + m.textarea.CursorUp() + } + m.textarea.SetCursor(0) // Only moves to the beginning of the row + return m +} + +func (m Model) SetSize(width, height int) Model { + m.width, m.height = width, height + m.textarea.SetWidth(width - 2) + m.textarea.SetHeight(height - 1) + return m +} + +func (m Model) KeyMap() help.KeyMap { + return nil +} + +func (m Model) Text() string { + return m.textarea.Value() +} diff --git a/internal/ui/style/style.go b/internal/core/ui/style/style.go similarity index 100% rename from internal/ui/style/style.go rename to internal/core/ui/style/style.go diff --git a/internal/ui/commit/cmd.go b/internal/domain/commit/cmd.go similarity index 91% rename from internal/ui/commit/cmd.go rename to internal/domain/commit/cmd.go index c8f40a5..dfd0f74 100644 --- a/internal/ui/commit/cmd.go +++ b/internal/domain/commit/cmd.go @@ -3,7 +3,7 @@ package commit import ( tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/git" + "github.com/michaelhass/gitglance/internal/core/git" ) // Execute creates a tea.Cmd to execute a git commit. diff --git a/internal/ui/commit/commit.go b/internal/domain/commit/commit.go similarity index 80% rename from internal/ui/commit/commit.go rename to internal/domain/commit/commit.go index c18f992..9a0f53e 100644 --- a/internal/ui/commit/commit.go +++ b/internal/domain/commit/commit.go @@ -6,12 +6,12 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/michaelhass/gitglance/internal/git" - "github.com/michaelhass/gitglance/internal/ui/container" - "github.com/michaelhass/gitglance/internal/ui/dialog" - "github.com/michaelhass/gitglance/internal/ui/list" - filelist "github.com/michaelhass/gitglance/internal/ui/list/file" - "github.com/michaelhass/gitglance/internal/ui/textinput" + "github.com/michaelhass/gitglance/internal/core/git" + "github.com/michaelhass/gitglance/internal/core/ui/components/container" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" + "github.com/michaelhass/gitglance/internal/core/ui/components/list" + filelist "github.com/michaelhass/gitglance/internal/core/ui/components/list/file" + "github.com/michaelhass/gitglance/internal/core/ui/components/textinput" ) // Model represents the UI to pefrom a commit. @@ -24,7 +24,7 @@ type Model struct { } func New(branch string, stagedFileList git.FileStatusList) Model { - fileListContent := list.NewContent( + fileListContent := list.NewContainerContent( list.New( "Staged", func(msg tea.Msg) tea.Cmd { return nil }, @@ -42,8 +42,8 @@ func New(branch string, stagedFileList git.FileStatusList) Model { fileListContent.Model, _ = fileListContent.SetItems(createListItems(stagedFileList)) - messageContainer := container.New( - textinput.NewContent( + messageContainer := textinput.NewContainer( + textinput.New( fmt.Sprintf("%s [%s]", "Commit", branch), "Enter commit message", ), @@ -77,7 +77,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { m, cmd = m.toggleFocus() cmds = append(cmds, cmd) case key.Matches(msg, m.keys.commit): - if mc, ok := m.message.Content().(textinput.Content); ok { + if mc, ok := m.message.Content().(textinput.ContainerContent); ok { return m, tea.Sequence(Execute(mc.Text()), dialog.Close()) } } @@ -114,9 +114,10 @@ func (m Model) Help() []key.Binding { } func (m Model) setMsg(msg string) (Model, tea.Cmd) { - if input, ok := m.message.Content().(textinput.Content); ok { - input = input.SetValue(msg) - input = input.SetCursorToStart() + if input, ok := m.message.Content().(textinput.ContainerContent); ok { + input.Model = input.Model. + SetValue(msg). + SetCursorToStart() m.message = m.message.SetContent(input) } return m, nil diff --git a/internal/domain/commit/dialog.go b/internal/domain/commit/dialog.go new file mode 100644 index 0000000..25e03a9 --- /dev/null +++ b/internal/domain/commit/dialog.go @@ -0,0 +1,37 @@ +package commit + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" +) + +// DialogContent is a wrapper to use the commit ui as dialog.DialogContent. +type DialogContent struct { + Model +} + +func NewContent(commit Model) DialogContent { + return DialogContent{ + Model: commit, + } +} + +func (dc DialogContent) Init() tea.Cmd { + return dc.Model.Init() +} + +func (dc DialogContent) Update(msg tea.Msg) (dialog.Content, tea.Cmd) { + model, cmd := dc.Model.Update(msg) + dc.Model = model + + return dc, cmd +} + +func (dc DialogContent) View() string { + return dc.Model.View() +} + +func (dc DialogContent) SetSize(width, height int) dialog.Content { + dc.Model = dc.Model.SetSize(width, height) + return dc +} diff --git a/internal/ui/commit/key.go b/internal/domain/commit/key.go similarity index 100% rename from internal/ui/commit/key.go rename to internal/domain/commit/key.go diff --git a/internal/domain/diff/container.go b/internal/domain/diff/container.go new file mode 100644 index 0000000..bcca841 --- /dev/null +++ b/internal/domain/diff/container.go @@ -0,0 +1,32 @@ +package diff + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/ui/components/container" +) + +// ContainerContent is a wrapper to use the commit ui as container.ContainerContent. +type ContainerContent struct { + Model +} + +func NewContent(model Model) ContainerContent { + return ContainerContent{Model: model} +} + +func (c ContainerContent) Update(msg tea.Msg) (container.Content, tea.Cmd) { + model, cmd := c.Model.Update(msg) + c.Model = model + return c, cmd +} + +func (c ContainerContent) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) { + model, cmd := c.Model.UpdateFocus(isFocused) + c.Model = model + return c, cmd +} + +func (c ContainerContent) SetSize(width, height int) container.Content { + c.Model = c.Model.SetSize(width, height) + return c +} diff --git a/internal/ui/diff/diff.go b/internal/domain/diff/diff.go similarity index 94% rename from internal/ui/diff/diff.go rename to internal/domain/diff/diff.go index 959d77a..409fde3 100644 --- a/internal/ui/diff/diff.go +++ b/internal/domain/diff/diff.go @@ -7,8 +7,8 @@ import ( "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/textwrap" - "github.com/michaelhass/gitglance/internal/ui/style" + "github.com/michaelhass/gitglance/internal/core/textwrap" + "github.com/michaelhass/gitglance/internal/core/ui/style" ) var ( diff --git a/internal/ui/diff/key.go b/internal/domain/diff/key.go similarity index 100% rename from internal/ui/diff/key.go rename to internal/domain/diff/key.go diff --git a/internal/domain/stash/cmd.go b/internal/domain/stash/cmd.go new file mode 100644 index 0000000..4e6d582 --- /dev/null +++ b/internal/domain/stash/cmd.go @@ -0,0 +1,52 @@ +package stash + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/git" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog/confirm" + "github.com/michaelhass/gitglance/internal/core/ui/components/list" +) + +type CreatedMsg struct { + err error +} + +func CreateWithUntracked() tea.Msg { + err := git.StashAll() + return CreatedMsg{err: err} +} + +func ShowCreateWithUntrackedConfirmation(onClose tea.Cmd) tea.Cmd { + confirmDialog := confirm.NewDialogConent( + confirm.New( + "Stash", "Do you want to stash all changes?", + CreateWithUntracked, + ), + ) + return dialog.Show(confirmDialog, onClose, dialog.CenterDisplayMode) +} + +type LoadedMsg struct { + Stash git.Stash + Err error +} + +func Load() tea.Msg { + stash, err := git.GetStash() + return LoadedMsg{Stash: stash, Err: err} +} + +func ShowApplyDialog(onClose tea.Cmd) tea.Cmd { + keyMap := list.NewKeyMap("", "apply stash", "") + keyMap.All.SetEnabled(false) + keyMap.Edit.SetEnabled(false) + keyMap.Delete.SetEnabled(false) + stashList := NewStashList("Apply stash", keyMap) + return dialog.Show(NewApplyDialogConent(stashList), onClose, dialog.CenterDisplayMode) +} + +func applyStashEntry(entry git.StashEntry) tea.Cmd { + _ = git.ApplyStashEntry(entry) + return dialog.Close() +} diff --git a/internal/domain/stash/dialog.go b/internal/domain/stash/dialog.go new file mode 100644 index 0000000..402d0e4 --- /dev/null +++ b/internal/domain/stash/dialog.go @@ -0,0 +1,61 @@ +package stash + +import ( + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" + "github.com/michaelhass/gitglance/internal/core/ui/style" +) + +const ( + titleHeight = 1 + borderPadding int = 1 +) + +var ( + titleStyle = style.Title.Height(titleHeight) + borderStyle = style.FocusBorder.PaddingLeft(borderPadding).PaddingRight(borderPadding) +) + +func NewApplyDialogConent(stashList StashList) ApplyDialogContent { + stashList.listModel, _ = stashList.listModel.UpdateFocus(true) + return ApplyDialogContent{StashList: stashList} +} + +func (dc ApplyDialogContent) Init() tea.Cmd { + return dc.StashList.Init() +} + +func (dc ApplyDialogContent) Update(msg tea.Msg) (dialog.Content, tea.Cmd) { + model, cmd := dc.StashList.Update(msg) + dc.StashList = model + return dc, cmd +} + +func (dc ApplyDialogContent) View() string { + title := titleStyle.Render(dc.StashList.Title()) + return borderStyle. + MaxHeight(dc.height). + MaxWidth(dc.width). + Render( + lipgloss.JoinVertical( + lipgloss.Left, + title, + "", + dc.StashList.View(), + ), + ) +} + +func (dc ApplyDialogContent) SetSize(width, height int) dialog.Content { + dc.width, dc.height = width, height + maxContentHeight := height - titleHeight - 1 - borderPadding*2 + maxContentWidth := width - borderPadding*2 + dc.StashList = dc.StashList.SetSize(maxContentWidth, maxContentHeight) + return dc +} + +func (dc ApplyDialogContent) Help() []key.Binding { + return dc.listModel.KeyMap().ShortHelp() +} diff --git a/internal/domain/stash/list.go b/internal/domain/stash/list.go new file mode 100644 index 0000000..3441a04 --- /dev/null +++ b/internal/domain/stash/list.go @@ -0,0 +1,79 @@ +package stash + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/michaelhass/gitglance/internal/core/git" + "github.com/michaelhass/gitglance/internal/core/ui/components/list" +) + +type StashListItem struct { + entry git.StashEntry +} + +func (item StashListItem) Render() string { + return item.entry.Message() +} + +type StashList struct { + listModel list.Model +} + +func NewStashList(title string, keyMap list.KeyMap) StashList { + listModel := list.New( + title, + func(msg tea.Msg) tea.Cmd { + switch msg := msg.(type) { + case list.SelectItemMsg: + if item, ok := msg.Item.(StashListItem); ok { + return applyStashEntry(item.entry) + } + return nil + } + return nil + }, + keyMap, + ) + return StashList{listModel: listModel} +} + +func (sl StashList) Init() tea.Cmd { + return Load +} + +func (sl StashList) Update(msg tea.Msg) (StashList, tea.Cmd) { + var cmds []tea.Cmd + + if msg, ok := msg.(LoadedMsg); ok { + var items []list.Item + for _, entry := range msg.Stash { + items = append(items, StashListItem{entry: entry}) + } + listModel, cmd := sl.listModel.SetItems(items) + sl.listModel = listModel + cmds = append(cmds, cmd) + } + + listModel, cmd := sl.listModel.Update(msg) + sl.listModel = listModel + cmds = append(cmds, cmd) + + return sl, tea.Batch(cmds...) +} + +func (sl StashList) View() string { + return sl.listModel.View() +} + +func (sl StashList) SetSize(width, height int) StashList { + sl.listModel = sl.listModel.SetSize(width, height) + return sl +} + +func (sl StashList) Title() string { + return sl.listModel.Title() +} + +type ApplyDialogContent struct { + StashList + width, height int +} diff --git a/internal/ui/status/cmd.go b/internal/page/status/cmd.go similarity index 82% rename from internal/ui/status/cmd.go rename to internal/page/status/cmd.go index d978090..3a3df74 100644 --- a/internal/ui/status/cmd.go +++ b/internal/page/status/cmd.go @@ -5,14 +5,15 @@ import ( "fmt" tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/editor" - "github.com/michaelhass/gitglance/internal/git" - "github.com/michaelhass/gitglance/internal/ui/commit" - "github.com/michaelhass/gitglance/internal/ui/confirm" - "github.com/michaelhass/gitglance/internal/ui/dialog" - "github.com/michaelhass/gitglance/internal/ui/list" - filelist "github.com/michaelhass/gitglance/internal/ui/list/file" - "github.com/michaelhass/gitglance/internal/ui/refresh" + "github.com/michaelhass/gitglance/internal/core/editor" + "github.com/michaelhass/gitglance/internal/core/git" + "github.com/michaelhass/gitglance/internal/core/refresh" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog" + "github.com/michaelhass/gitglance/internal/core/ui/components/dialog/confirm" + "github.com/michaelhass/gitglance/internal/core/ui/components/list" + filelist "github.com/michaelhass/gitglance/internal/core/ui/components/list/file" + "github.com/michaelhass/gitglance/internal/domain/commit" + "github.com/michaelhass/gitglance/internal/domain/stash" ) type initializedMsg struct { @@ -128,12 +129,7 @@ func deleteFile(fileItem filelist.Item) tea.Cmd { }), list.ForceFocusUpdate, ) - confirmDialog := confirm.New( - title, - msg, - confirmCmd, - ) - + confirmDialog := confirm.NewDialogConent(confirm.New(title, msg, confirmCmd)) return dialog.Show(confirmDialog, nil, dialog.CenterDisplayMode) } @@ -210,17 +206,9 @@ func showCommitDialog(branchName string, files git.FileStatusList) tea.Cmd { } func showStashAllConfirmation() tea.Cmd { - confirm := confirm.New("Stash", "Do you want to stash all changes?", stashAll()) - return dialog.Show(confirm, refreshStatus(), dialog.CenterDisplayMode) -} - -type stashedMsg struct { - err error + return stash.ShowCreateWithUntrackedConfirmation(refreshStatus()) } -func stashAll() tea.Cmd { - return func() tea.Msg { - err := git.StashAll() - return stashedMsg{err: err} - } +func showApplyStashDialog() tea.Cmd { + return stash.ShowApplyDialog(refreshStatus()) } diff --git a/internal/ui/status/key.go b/internal/page/status/key.go similarity index 100% rename from internal/ui/status/key.go rename to internal/page/status/key.go diff --git a/internal/ui/status/status.go b/internal/page/status/status.go similarity index 90% rename from internal/ui/status/status.go rename to internal/page/status/status.go index 8fc30f1..8c52f17 100644 --- a/internal/ui/status/status.go +++ b/internal/page/status/status.go @@ -7,14 +7,14 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/michaelhass/gitglance/internal/git" - "github.com/michaelhass/gitglance/internal/ui/container" - "github.com/michaelhass/gitglance/internal/ui/diff" - "github.com/michaelhass/gitglance/internal/ui/exit" - "github.com/michaelhass/gitglance/internal/ui/list" - filelist "github.com/michaelhass/gitglance/internal/ui/list/file" - "github.com/michaelhass/gitglance/internal/ui/refresh" - "github.com/michaelhass/gitglance/internal/ui/style" + "github.com/michaelhass/gitglance/internal/core/exit" + "github.com/michaelhass/gitglance/internal/core/git" + "github.com/michaelhass/gitglance/internal/core/refresh" + "github.com/michaelhass/gitglance/internal/core/ui/components/container" + "github.com/michaelhass/gitglance/internal/core/ui/components/list" + filelist "github.com/michaelhass/gitglance/internal/core/ui/components/list/file" + "github.com/michaelhass/gitglance/internal/core/ui/style" + "github.com/michaelhass/gitglance/internal/domain/diff" ) type section byte @@ -125,7 +125,7 @@ func New() Model { help := help.New() help.ShowAll = false - unstagedFileList := list.NewContent( + unstagedFileList := list.NewContainerContent( list.New("Unstaged", unstagedFilesItemHandler, list.NewKeyMap( "stage all", "stage file", @@ -139,7 +139,7 @@ func New() Model { ) stagedFileListKeyMap.Delete.SetEnabled(false) - stagedFileList := list.NewContent(list.New("Staged", stagedFilesItemHandler, stagedFileListKeyMap)) + stagedFileList := list.NewContainerContent(list.New("Staged", stagedFilesItemHandler, stagedFileListKeyMap)) diffContent := diff.NewContent(diff.New()) return Model{ @@ -208,6 +208,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { cmds = append(cmds, refreshStatus()) case key.Matches(msg, m.keys.stash): cmds = append(cmds, showStashAllConfirmation()) + case key.Matches(msg, key.NewBinding(key.WithKeys("S"))): + cmds = append(cmds, showApplyStashDialog()) } } @@ -284,13 +286,13 @@ func (m Model) handleStatusUpdateMsg(msg statusUpdateMsg) (Model, tea.Cmd) { return m, exit.WithMsg(msg.Err.Error()) } - if section, ok := m.sections[unstagedSection].Content().(list.Content); ok { + if section, ok := m.sections[unstagedSection].Content().(list.ContainerContent); ok { model, cmd := section.SetItems(createListItems(m.workTreeStatus.UnstagedFiles(), false)) section.Model = model cmds = append(cmds, cmd) m.sections[unstagedSection] = m.sections[unstagedSection].SetContent(section) } - if section, ok := m.sections[stagedSection].Content().(list.Content); ok { + if section, ok := m.sections[stagedSection].Content().(list.ContainerContent); ok { model, cmd := section.SetItems(createListItems(m.workTreeStatus.StagedFiles(), true)) model = model.SetTitle(fmt.Sprintf("Staged [%s]", m.workTreeStatus.CleanedBranchName)) section.Model = model @@ -304,7 +306,7 @@ func (m Model) handleStatusUpdateMsg(msg statusUpdateMsg) (Model, tea.Cmd) { } func (m Model) handleLoadedDiffMsg(msg loadedDiffMsg) (Model, tea.Cmd) { - section, ok := m.sections[diffSection].Content().(diff.Content) + section, ok := m.sections[diffSection].Content().(diff.ContainerContent) if !ok { return m, nil } diff --git a/internal/ui/commit/content.go b/internal/ui/commit/content.go deleted file mode 100644 index c6a665f..0000000 --- a/internal/ui/commit/content.go +++ /dev/null @@ -1,37 +0,0 @@ -package commit - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/ui/dialog" -) - -// Content is a wrapper to use the commit ui as dialog.Content. -type Content struct { - Model -} - -func NewContent(commit Model) Content { - return Content{ - Model: commit, - } -} - -func (c Content) Init() tea.Cmd { - return c.Model.Init() -} - -func (c Content) Update(msg tea.Msg) (dialog.Content, tea.Cmd) { - model, cmd := c.Model.Update(msg) - c.Model = model - - return c, cmd -} - -func (c Content) View() string { - return c.Model.View() -} - -func (c Content) SetSize(width, height int) dialog.Content { - c.Model = c.Model.SetSize(width, height) - return c -} diff --git a/internal/ui/diff/content.go b/internal/ui/diff/content.go deleted file mode 100644 index 41fd5f0..0000000 --- a/internal/ui/diff/content.go +++ /dev/null @@ -1,32 +0,0 @@ -package diff - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/ui/container" -) - -// Content is a wrapper to use the commit ui as container.Content. -type Content struct { - Model -} - -func NewContent(model Model) Content { - return Content{Model: model} -} - -func (c Content) Update(msg tea.Msg) (container.Content, tea.Cmd) { - model, cmd := c.Model.Update(msg) - c.Model = model - return c, cmd -} - -func (c Content) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) { - model, cmd := c.Model.UpdateFocus(isFocused) - c.Model = model - return c, cmd -} - -func (c Content) SetSize(width, height int) container.Content { - c.Model = c.Model.SetSize(width, height) - return c -} diff --git a/internal/ui/list/content.go b/internal/ui/list/content.go deleted file mode 100644 index 77ec468..0000000 --- a/internal/ui/list/content.go +++ /dev/null @@ -1,32 +0,0 @@ -package list - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/michaelhass/gitglance/internal/ui/container" -) - -// Content is a wrapper to use the filelist ui as container.Content. -type Content struct { - Model -} - -func NewContent(model Model) Content { - return Content{Model: model} -} - -func (c Content) Update(msg tea.Msg) (container.Content, tea.Cmd) { - model, cmd := c.Model.Update(msg) - c.Model = model - return c, cmd -} - -func (c Content) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) { - model, cmd := c.Model.UpdateFocus(isFocused) - c.Model = model - return c, cmd -} - -func (c Content) SetSize(width, height int) container.Content { - c.Model = c.Model.SetSize(width, height) - return c -} diff --git a/internal/ui/textinput/content.go b/internal/ui/textinput/content.go deleted file mode 100644 index 4771505..0000000 --- a/internal/ui/textinput/content.go +++ /dev/null @@ -1,130 +0,0 @@ -package textinput - -import ( - "fmt" - - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/textarea" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/michaelhass/gitglance/internal/ui/container" - "github.com/michaelhass/gitglance/internal/ui/style" -) - -var ( - countStyle = style.SublteText.Height(1) -) - -type Content struct { - title string - - textarea textarea.Model - - width int - height int - - isFocused bool -} - -func NewContent(title string, placeholder string) Content { - var ( - textarea = textarea.New() - blurredStyle = textarea.BlurredStyle - focusedStyle = textarea.FocusedStyle - ) - - textarea.Placeholder = placeholder - textarea.Prompt = "" - textarea.ShowLineNumbers = false - - blurredStyle.Text = style.SublteText - blurredStyle.Placeholder = style.SublteText - blurredStyle.CursorLine = lipgloss.NewStyle() - textarea.BlurredStyle = blurredStyle - - focusedStyle.Placeholder = style.SublteText - focusedStyle.Text = style.Text - focusedStyle.CursorLine = lipgloss.NewStyle() - textarea.FocusedStyle = focusedStyle - - return Content{ - title: title, - textarea: textarea, - } -} - -func (c Content) Init() tea.Cmd { - return nil -} - -func (c Content) Update(msg tea.Msg) (container.Content, tea.Cmd) { - textarea, cmd := c.textarea.Update(msg) - c.textarea = textarea - return c, cmd -} - -func (c Content) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) { - c.isFocused = isFocused - if isFocused { - c.textarea.Focus() - } else { - c.textarea.Blur() - } - return c, nil -} - -func (c Content) View() string { - inputLength := len([]rune(c.textarea.Value())) - - count := countStyle. - MaxWidth(c.width - 2). - Render(fmt.Sprint("Chararcters ", inputLength)) - - countLine := lipgloss.PlaceHorizontal( - c.width-2, - lipgloss.Right, - count, - ) - - return lipgloss.JoinVertical( - lipgloss.Top, - c.textarea.View(), - countLine, - ) -} - -func (c Content) Title() string { - return c.title -} - -func (c Content) SetValue(value string) Content { - c.textarea.SetValue(value) - return c -} - -func (c Content) SetCursorToStart() Content { - // textarea.Line() does not seem to return the correct current - // line of the cursor. At least after setting a new value. - // Thus, move the cursor up more than potentially needed to ensure - // that we are at the very beginning of the text input. - for i := 0; i < c.textarea.LineCount(); i++ { - c.textarea.CursorUp() - } - c.textarea.SetCursor(0) // Only moves to the beginning of the row - return c -} - -func (c Content) SetSize(width, height int) container.Content { - c.width, c.height = width, height - c.textarea.SetWidth(width - 2) - c.textarea.SetHeight(height - 1) - return c -} - -func (c Content) KeyMap() help.KeyMap { - return nil -} - -func (c Content) Text() string { - return c.textarea.Value() -} diff --git a/main.go b/main.go index a15943a..f693574 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/michaelhass/gitglance/internal/ui/app" + "github.com/michaelhass/gitglance/internal/app" ) func main() {