Skip to content

Commit

Permalink
feat: update cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
mistakenelf committed Mar 5, 2024
1 parent dc1d338 commit f00a0df
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 55 deletions.
4 changes: 1 addition & 3 deletions examples/filetree/filetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"log"
"os"

tea "github.com/charmbracelet/bubbletea"
"github.com/mistakenelf/teacup/filetree"
Expand All @@ -15,8 +14,7 @@ type model struct {

// New creates a new instance of the UI.
func New() model {
wd, _ := os.Getwd()
filetree := filetree.New(wd, true)
filetree := filetree.New()

return model{
filetree: filetree,
Expand Down
232 changes: 180 additions & 52 deletions filetree/filetree.go
Original file line number Diff line number Diff line change
@@ -1,87 +1,215 @@
package filetree

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"

"github.com/charmbracelet/bubbles/filepicker"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/mistakenelf/teacup/filesystem"
)

type Model struct {
filepicker filepicker.Model
selectedFile string
quitting bool
err error
active bool
}
const (
thousand = 1000
ten = 10
fivePercent = 0.0499
)

type clearErrorMsg struct{}
var (
selectedItemStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true)
)

func clearErrorAfter(t time.Duration) tea.Cmd {
return tea.Tick(t, func(_ time.Time) tea.Msg {
return clearErrorMsg{}
})
type KeyMap struct {

Check warning on line 26 in filetree/filetree.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported type KeyMap should have comment or be unexported (revive)
Down key.Binding
Up key.Binding
}

func New(currentDirectory string, active bool) Model {
fp := filepicker.New()
fp.CurrentDirectory = currentDirectory

return Model{
filepicker: fp,
selectedFile: "",
quitting: false,
err: nil,
active: active,
}
type DirectoryItem struct {

Check warning on line 31 in filetree/filetree.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported type DirectoryItem should have comment or be unexported (revive)
name string
details string
path string
extension string
isDirectory bool
currentDirectory string
}

func (m Model) Init() tea.Cmd {
return m.filepicker.Init()
type getDirectoryListingMsg []DirectoryItem
type errorMsg error

type Model struct {

Check warning on line 43 in filetree/filetree.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported type Model should have comment or be unexported (revive)
viewport viewport.Model
cursor int
files []DirectoryItem
active bool
keyMap KeyMap
}

func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
switch msg := msg.(type) {
case clearErrorMsg:
m.err = nil
case tea.WindowSizeMsg:
m.filepicker.Height = msg.Height
func DefaultKeyMap() KeyMap {

Check warning on line 51 in filetree/filetree.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported function DefaultKeyMap should have comment or be unexported (revive)
return KeyMap{
Down: key.NewBinding(key.WithKeys("j", "down", "ctrl+n"), key.WithHelp("j", "down")),
Up: key.NewBinding(key.WithKeys("k", "up", "ctrl+p"), key.WithHelp("k", "up")),
}
}

var cmd tea.Cmd
func New() Model {

Check warning on line 58 in filetree/filetree.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported function New should have comment or be unexported (revive)
viewPort := viewport.New(0, 0)

if m.active {
m.filepicker, cmd = m.filepicker.Update(msg)
return Model{
viewport: viewPort,
cursor: 0,
active: true,
keyMap: DefaultKeyMap(),
}
}

if didSelect, path := m.filepicker.DidSelectFile(msg); didSelect {
m.selectedFile = path
// ConvertBytesToSizeString converts a byte count to a human readable string.
func ConvertBytesToSizeString(size int64) string {
if size < thousand {
return fmt.Sprintf("%dB", size)
}

if didSelect, path := m.filepicker.DidSelectDisabledFile(msg); didSelect {
m.err = errors.New(path + " is not valid.")
m.selectedFile = ""
suffix := []string{
"K", // kilo
"M", // mega
"G", // giga
"T", // tera
"P", // peta
"E", // exa
"Z", // zeta
"Y", // yotta
}

return m, tea.Batch(cmd, clearErrorAfter(2*time.Second))
curr := float64(size) / thousand
for _, s := range suffix {
if curr < ten {
return fmt.Sprintf("%.1f%s", curr-fivePercent, s)
} else if curr < thousand {
return fmt.Sprintf("%d%s", int(curr), s)
}
curr /= thousand
}

return m, cmd
return ""
}

func (m Model) View() string {
var s strings.Builder
// SetSize sets the size of the bubble.
func (m *Model) SetSize(w, h int) {
m.viewport.Width = w
m.viewport.Height = h
}

// SetIsActive sets if the bubble is currently active.
func (m *Model) SetIsActive(active bool) {
m.active = active
}

// GotoTop jumps to the top of the viewport.
func (m *Model) GotoTop() {
m.viewport.GotoTop()
}

if m.quitting {
return ""
// getDirectoryListingCmd updates the directory listing based on the name of the directory provided.
func getDirectoryListingCmd(directoryName string, showHidden bool) tea.Cmd {
return func() tea.Msg {
var err error
var directoryItems []DirectoryItem

if directoryName == filesystem.HomeDirectory {
directoryName, err = filesystem.GetHomeDirectory()
if err != nil {
return errorMsg(err)
}
}

directoryInfo, err := os.Stat(directoryName)
if err != nil {
return errorMsg(err)
}

if !directoryInfo.IsDir() {
return nil
}

files, err := filesystem.GetDirectoryListing(directoryName, showHidden)
if err != nil {
return errorMsg(err)
}

err = os.Chdir(directoryName)
if err != nil {
return errorMsg(err)
}

workingDirectory, err := filesystem.GetWorkingDirectory()
if err != nil {
return errorMsg(err)
}

for _, file := range files {
fileInfo, err := file.Info()
if err != nil {
continue
}

status := fmt.Sprintf("%s %s %s",
fileInfo.ModTime().Format("2006-01-02 15:04:05"),
fileInfo.Mode().String(),
ConvertBytesToSizeString(fileInfo.Size()))

directoryItems = append(directoryItems, DirectoryItem{
name: file.Name(),
details: status,
path: filepath.Join(workingDirectory, file.Name()),
extension: filepath.Ext(fileInfo.Name()),
isDirectory: fileInfo.IsDir(),
currentDirectory: workingDirectory,
})
}

return getDirectoryListingMsg(directoryItems)
}
}

func (m Model) Init() tea.Cmd {

Check warning on line 177 in filetree/filetree.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported method Model.Init should have comment or be unexported (revive)
return getDirectoryListingCmd(filesystem.CurrentDirectory, true)
}

if m.err != nil {
s.WriteString(m.filepicker.Styles.DisabledFile.Render(m.err.Error()))
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {

switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.viewport.Width = msg.Width
m.viewport.Height = msg.Height
case getDirectoryListingMsg:
if msg != nil {
m.files = msg
}
case tea.KeyMsg:
switch {
case key.Matches(msg, m.keyMap.Up):
m.cursor--
case key.Matches(msg, m.keyMap.Down):
m.cursor++
}
}

s.WriteString("\n" + m.filepicker.View())
return m, nil
}

func (m Model) View() string {
var fileList strings.Builder

for i, file := range m.files {
if i == m.cursor {
fileList.WriteString(selectedItemStyle.Render(file.name) + "\n")
} else {
fileList.WriteString(file.name + "\n")
}
}

return s.String()
return fileList.String()
}

0 comments on commit f00a0df

Please sign in to comment.