diff --git a/cmd/filter/filter.go b/cmd/filter/filter.go index f83de47..a4e312c 100644 --- a/cmd/filter/filter.go +++ b/cmd/filter/filter.go @@ -9,38 +9,27 @@ import ( "git.andreafazzi.eu/andrea/probo/pkg/store/file" "github.com/alecthomas/chroma/quick" - "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/itchyny/gojq" + foam "github.com/remogatto/sugarfoam" "github.com/remogatto/sugarfoam/components/group" "github.com/remogatto/sugarfoam/components/header" + "github.com/remogatto/sugarfoam/components/help" "github.com/remogatto/sugarfoam/components/statusbar" "github.com/remogatto/sugarfoam/components/textinput" "github.com/remogatto/sugarfoam/components/viewport" "github.com/remogatto/sugarfoam/layout" ) -type storeLoadedMsg struct { - store []any -} - -type resultMsg struct { - result []any -} - -type errorMsg struct { - error error -} - type FilterModel struct { // UI textInput *textinput.Model viewport *viewport.Model group *group.Model - help help.Model + help *help.Model statusBar *statusbar.Model spinner spinner.Model @@ -68,54 +57,6 @@ type FilterModel struct { filterType string } -type keyBindings struct { - group *group.Model - - quit, enter key.Binding -} - -func (k *keyBindings) ShortHelp() []key.Binding { - keys := make([]key.Binding, 0) - - current := k.group.Current() - - switch item := current.(type) { - case *viewport.Model: - keys = append( - keys, - item.KeyMap.Up, - item.KeyMap.Down, - ) - } - - keys = append( - keys, - k.quit, - ) - - return keys -} - -func (k keyBindings) FullHelp() [][]key.Binding { - return [][]key.Binding{ - { - k.quit, - }, - } -} - -func newBindings(g *group.Model) *keyBindings { - return &keyBindings{ - group: g, - quit: key.NewBinding( - key.WithKeys("esc"), key.WithHelp("esc", "Quit app"), - ), - enter: key.NewBinding( - key.WithKeys("enter"), key.WithHelp("enter", "Quit app and return the results"), - ), - } -} - func New(path string, filterType string, stdin string) *FilterModel { textInput := textinput.New( textinput.WithPlaceholder("Write your jq filter here..."), @@ -123,8 +64,6 @@ func New(path string, filterType string, stdin string) *FilterModel { viewport := viewport.New() - help := help.New() - group := group.New( group.WithItems(textInput, viewport), group.WithLayout( @@ -154,10 +93,15 @@ func New(path string, filterType string, stdin string) *FilterModel { ), ) + help := help.New( + bindings, + help.WithStyles(&foam.Styles{NoBorder: lipgloss.NewStyle().Padding(1, 1)})) + document := layout.New( layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Margin(1)}), layout.WithItem(header), layout.WithItem(group), + layout.WithItem(help), layout.WithItem(statusBar), ) diff --git a/cmd/filter/keymap.go b/cmd/filter/keymap.go new file mode 100644 index 0000000..cfc9010 --- /dev/null +++ b/cmd/filter/keymap.go @@ -0,0 +1,55 @@ +package filter + +import ( + "github.com/charmbracelet/bubbles/key" + "github.com/remogatto/sugarfoam/components/group" + "github.com/remogatto/sugarfoam/components/viewport" +) + +type keyBindings struct { + group *group.Model + + quit, enter key.Binding +} + +func (k *keyBindings) ShortHelp() []key.Binding { + keys := make([]key.Binding, 0) + + current := k.group.Current() + + switch item := current.(type) { + case *viewport.Model: + keys = append( + keys, + item.KeyMap.Up, + item.KeyMap.Down, + ) + } + + keys = append( + keys, + k.quit, + ) + + return keys +} + +func (k keyBindings) FullHelp() [][]key.Binding { + return [][]key.Binding{ + { + k.quit, + }, + } +} + +func newBindings(g *group.Model) *keyBindings { + return &keyBindings{ + group: g, + quit: key.NewBinding( + key.WithKeys("esc"), key.WithHelp("esc", "Quit app"), + ), + enter: key.NewBinding( + key.WithKeys("enter"), key.WithHelp("enter", "Quit app and return the results"), + ), + } +} diff --git a/cmd/filter/message.go b/cmd/filter/message.go new file mode 100644 index 0000000..2c53992 --- /dev/null +++ b/cmd/filter/message.go @@ -0,0 +1,13 @@ +package filter + +type storeLoadedMsg struct { + store []any +} + +type resultMsg struct { + result []any +} + +type errorMsg struct { + error error +} diff --git a/cmd/session.go b/cmd/session.go index df235f5..3b30bd8 100644 --- a/cmd/session.go +++ b/cmd/session.go @@ -42,7 +42,7 @@ func updateSession(cmd *cobra.Command, args []string) { lipgloss.SetColorProfile(termenv.TrueColor) - model, err := tea.NewProgram( + _, err = tea.NewProgram( session.New(path, util.ReadStdin()), tea.WithOutput(os.Stderr), ).Run() @@ -50,10 +50,5 @@ func updateSession(cmd *cobra.Command, args []string) { fmt.Println("Error running program:", err) os.Exit(1) } - result := model.(*session.SessionModel) - if result.Result != "" { - fmt.Fprintf(os.Stdout, result.Result) - } - fmt.Println("session called", path, model) } diff --git a/cmd/session/format.go b/cmd/session/format.go index 6f99728..b3b27fa 100644 --- a/cmd/session/format.go +++ b/cmd/session/format.go @@ -2,7 +2,7 @@ package session var ( stateFormats = map[int][]string{ - BrowseState: []string{"BROWSE 📖", "Use the navigation keys to browse the content. Press enter to return the result.", "STORE 🟢"}, + BrowseState: []string{"BROWSE 📖", "Total sessions in the store: %d, Exams in the current session: %d", "STORE 🟢"}, LoadingStoreState: []string{"LOAD %s", "Loading the store...", "STORE 🔴"}, ErrorState: []string{"ERROR 📖", "%v", "STORE 🟢"}, } diff --git a/cmd/session/keymap.go b/cmd/session/keymap.go new file mode 100644 index 0000000..c4010c9 --- /dev/null +++ b/cmd/session/keymap.go @@ -0,0 +1,69 @@ +package session + +import ( + "github.com/charmbracelet/bubbles/key" + "github.com/remogatto/sugarfoam/components/form" + "github.com/remogatto/sugarfoam/components/group" + "github.com/remogatto/sugarfoam/components/table" + "github.com/remogatto/sugarfoam/components/viewport" +) + +type keyBindings struct { + group *group.Model + + quit, enter key.Binding +} + +func (k *keyBindings) ShortHelp() []key.Binding { + keys := make([]key.Binding, 0) + + current := k.group.Current() + + switch item := current.(type) { + case *table.Model: + keys = append( + keys, + item.KeyMap.LineUp, + item.KeyMap.LineDown, + ) + + case *viewport.Model: + keys = append( + keys, + item.KeyMap.Up, + item.KeyMap.Down, + ) + case *form.Model: + keys = append( + keys, + item.KeyBinds()..., + ) + + } + + keys = append( + keys, + k.group.KeyMap.FocusNext, + k.group.KeyMap.FocusPrev, + k.quit, + ) + + return keys +} + +func (k keyBindings) FullHelp() [][]key.Binding { + return [][]key.Binding{ + { + k.quit, + }, + } +} + +func newBindings(g *group.Model) *keyBindings { + return &keyBindings{ + group: g, + quit: key.NewBinding( + key.WithKeys("esc"), key.WithHelp("esc", "quit app"), + ), + } +} diff --git a/cmd/session/session.go b/cmd/session/session.go index 9e56e29..5ea746f 100644 --- a/cmd/session/session.go +++ b/cmd/session/session.go @@ -3,6 +3,7 @@ package session import ( "bytes" "encoding/json" + "errors" "fmt" "os" "strings" @@ -10,7 +11,7 @@ import ( "git.andreafazzi.eu/andrea/probo/pkg/models" "git.andreafazzi.eu/andrea/probo/pkg/store/file" "github.com/alecthomas/chroma/quick" - "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/spinner" btTable "github.com/charmbracelet/bubbles/table" @@ -20,9 +21,11 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/d5/tengo/v2" "github.com/d5/tengo/v2/stdlib" + foam "github.com/remogatto/sugarfoam" "github.com/remogatto/sugarfoam/components/form" "github.com/remogatto/sugarfoam/components/group" "github.com/remogatto/sugarfoam/components/header" + "github.com/remogatto/sugarfoam/components/help" "github.com/remogatto/sugarfoam/components/statusbar" "github.com/remogatto/sugarfoam/components/table" "github.com/remogatto/sugarfoam/components/viewport" @@ -36,7 +39,7 @@ type SessionModel struct { viewport *viewport.Model table *table.Model group *group.Model - help help.Model + help *help.Model statusBar *statusbar.Model spinner spinner.Model @@ -46,14 +49,14 @@ type SessionModel struct { // Key bindings bindings *keyBindings - // file store - store *file.SessionFileStore + // store + store *file.SessionFileStore + lenStore int + result []any // json - FilteredJson string - InputJson string - Result string + InputJson string // session session *models.Session @@ -67,65 +70,29 @@ type SessionModel struct { state int } -type keyBindings struct { - group *group.Model - - quit, enter key.Binding -} - -func (k *keyBindings) ShortHelp() []key.Binding { - keys := make([]key.Binding, 0) - - current := k.group.Current() - - switch item := current.(type) { - case *viewport.Model: - keys = append( - keys, - item.KeyMap.Up, - item.KeyMap.Down, - ) - } - - keys = append( - keys, - k.quit, - ) - - return keys -} - -func (k keyBindings) FullHelp() [][]key.Binding { - return [][]key.Binding{ - { - k.quit, - }, - } -} - -func newBindings(g *group.Model) *keyBindings { - return &keyBindings{ - group: g, - quit: key.NewBinding( - key.WithKeys("esc"), key.WithHelp("esc", "Quit app"), - ), - enter: key.NewBinding( - key.WithKeys("enter"), key.WithHelp("enter", "Quit app and return the results"), - ), - } -} - func New(path string, stdin string) *SessionModel { form := form.New( form.WithGroups(huh.NewGroup( huh.NewInput(). - Title("Session name"). - Description("Enter the name of the session"), + Key("sessionTitle"). + Title("Session title"). + Description("Enter the title of the session"). + Validate(func(str string) error { + if str == "" { + return errors.New("You must set a session name!") + } + return nil + + }), ))) - form. - WithShowHelp(false). - WithTheme(huh.ThemeDracula()) + formBinding := huh.NewDefaultKeyMap() + formBinding.Input.Next = key.NewBinding(key.WithKeys("down"), key.WithHelp("down", "next")) + formBinding.Input.Prev = key.NewBinding(key.WithKeys("up"), key.WithHelp("up", "prev")) + formBinding.Confirm.Next = key.NewBinding(key.WithKeys("down"), key.WithHelp("down", "next")) + formBinding.Confirm.Prev = key.NewBinding(key.WithKeys("up"), key.WithHelp("up", "prev")) + + form.WithShowHelp(false).WithTheme(huh.ThemeDracula()).WithKeyMap(formBinding) viewport := viewport.New() @@ -137,13 +104,11 @@ func New(path string, stdin string) *SessionModel { {Title: "Class", Width: 20}, }) - help := help.New() - group := group.New( group.WithItems(form, table, viewport), group.WithLayout( layout.New( - layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Padding(1, 0, 1, 0)}), + layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Padding(1, 1)}), layout.WithItem(form), layout.WithItem(tiled.New(table, viewport)), ), @@ -168,10 +133,15 @@ func New(path string, stdin string) *SessionModel { ), ) + help := help.New( + bindings, + help.WithStyles(&foam.Styles{NoBorder: lipgloss.NewStyle().Padding(1, 1)})) + document := layout.New( layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Margin(1)}), layout.WithItem(header), layout.WithItem(group), + layout.WithItem(help), layout.WithItem(statusBar), ) @@ -221,12 +191,7 @@ func (m *SessionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch { case key.Matches(msg, m.bindings.quit): - m.FilteredJson = "" - return m, tea.Quit - - case key.Matches(msg, m.bindings.enter): - m.marshalJSON() - return m, tea.Quit + cmds = append(cmds, tea.Quit) } case storeLoadedMsg: @@ -271,7 +236,6 @@ func (m *SessionModel) executeScript(path string) tea.Cmd { _ = s.Add("input", m.InputJson) _ = s.Add("output", string(sessionJson)) - // compile the source c, err := s.Compile() if err != nil { return errorMsg{err} @@ -286,20 +250,15 @@ func (m *SessionModel) executeScript(path string) tea.Cmd { } } -func (m *SessionModel) marshalJSON() { - var result interface{} +func (m *SessionModel) createSession() error { + m.session.Title = m.form.GetString("sessionTitle") - err := json.Unmarshal([]byte(m.FilteredJson), &result) + _, err := m.store.Create(m.session) if err != nil { - panic(err) + return err } - resultJson, err := json.Marshal(result) - if err != nil { - panic(err) - } - m.Result = string(resultJson) - + return nil } func (m *SessionModel) showErrorOnStatusBar(err error) { @@ -322,14 +281,16 @@ func (m *SessionModel) updateTableContent(session *models.Session) { }) } + m.table.SetRows(rows) } func (m *SessionModel) updateViewportContent(session *models.Session) { currentToken := m.table.SelectedRow()[0] currentExam := session.Exams[currentToken] + if currentExam == nil { - panic("Current token is not associate to any exam") + panic("Current token is not associate to any exam!") } md, err := currentExam.ToMarkdown() @@ -392,7 +353,9 @@ func (m *SessionModel) handleScriptExecuted(msg tea.Msg) { func (m *SessionModel) handleStoreLoaded(msg tea.Msg) tea.Cmd { storeMsg := msg.(storeLoadedMsg) + m.store = storeMsg.store + m.lenStore = len(m.store.ReadAll()) return m.executeScript(m.scriptFilePath) @@ -405,16 +368,37 @@ func (m *SessionModel) handleState(msg tea.Msg, cmds []tea.Cmd) []tea.Cmd { return m.updateSpinner(msg, cmd, cmds) } + if m.form.State == huh.StateCompleted { + err := m.createSession() + if err != nil { + panic(err) + } + cmds = append(cmds, tea.Quit) + } + + if len(m.form.Errors()) > 0 { + m.state = ErrorState + for _, err := range m.form.Errors() { + m.showErrorOnStatusBar(err) + } + } else { + m.state = BrowseState + } + if m.state == BrowseState { m.updateViewportContent(m.session) } - cmds = append(cmds, cmd /*, m.query(m.textInput.Value())*/) - if m.state != ErrorState { - m.statusBar.SetContent(stateFormats[BrowseState]...) + m.statusBar.SetContent( + stateFormats[BrowseState][0], + fmt.Sprintf(stateFormats[BrowseState][1], m.lenStore, len(m.session.Exams)), + stateFormats[BrowseState][2], + ) } + cmds = append(cmds, cmd) + return cmds } @@ -456,13 +440,6 @@ func toColoredJson(data []any) (string, error) { return sanitize(buffer.String()), nil } -func toJson(data []any) (string, error) { - result, err := json.Marshal(data) - if err != nil { - return "", err - } - return string(result), nil -} func sanitize(text string) string { // FIXME: The use of a standard '-' character causes rendering // issues within the viewport. Further investigation is diff --git a/pkg/models/session.go b/pkg/models/session.go index bf566dd..49ff185 100644 --- a/pkg/models/session.go +++ b/pkg/models/session.go @@ -9,7 +9,7 @@ import ( type Session struct { Meta - Title string `json:"name"` + Title string `json:"title"` Exams map[string]*Exam `json:"exams"` } diff --git a/pkg/store/store.go b/pkg/store/store.go index 295e60d..81ca416 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -30,12 +30,6 @@ type Storer[T Storable] interface { Json() ([]byte, error) } -// type FilterStorer[T Storable] interface { -// Storer[T] - -// Filter([]T, func(T) bool) []T -// } - type Store[T Storable] struct { ids map[string]T hashes map[string]T