Skip to content

Commit

Permalink
wizard model draft
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Oct 27, 2024
1 parent a3e4a5b commit 8cf2227
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 154 deletions.
13 changes: 8 additions & 5 deletions cmd/slackdump/internal/diag/wizdebug.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import (

tea "github.com/charmbracelet/bubbletea"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/golang/base"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/cfgui"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/dumpui"
)

var CmdWizDebug = &base.Command{
UsageLine: "slackdump tools wizdebug",
Short: "run the wizard debug command",
Run: runWizDebug,
UsageLine: "slackdump tools wizdebug",
Short: "run the wizard debug command",
Run: runWizDebug,
PrintFlags: true,
}

func runWizDebug(ctx context.Context, cmd *base.Command, args []string) error {
Expand All @@ -21,8 +23,9 @@ func runWizDebug(ctx context.Context, cmd *base.Command, args []string) error {
Help: "Run the command",
},
{
Name: "Global Configuration...",
Help: "Set global configuration options",
Name: "Global Configuration...",
Help: "Set global configuration options",
Model: cfgui.NewConfigUI(cfgui.DefaultStyle(), cfgui.GlobalConfig).(dumpui.FocusModel), // TODO: filthy cast
},
{
Name: "Local Configuration...",
Expand Down
31 changes: 4 additions & 27 deletions cmd/slackdump/internal/ui/cfgui/cfgui.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

tea "github.com/charmbracelet/bubbletea"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui"
)

// Global initialises and runs the configuration UI.
Expand All @@ -14,34 +13,12 @@ func Global(ctx context.Context) error {
return err
}

func GlobalConfig() Configuration {
return globalConfig()
}

func Local(ctx context.Context, cfgFn func() Configuration) error {
p := tea.NewProgram(NewConfigUI(DefaultStyle(), cfgFn))
_, err := p.Run()
return err
}

func NewConfigUI(sty Style, cfgFn func() Configuration) tea.Model {
end := 0
for _, group := range cfgFn() {
end += len(group.Params)
}
end--
return configmodel{
cfgFn: cfgFn,
end: end,
Style: sty,
}
}

func DefaultStyle() Style {
return Style{
Border: ui.DefaultTheme().Focused.Border,
Title: ui.DefaultTheme().Focused.Options.Section,
Description: ui.DefaultTheme().Focused.Description,
Name: ui.DefaultTheme().Focused.Options.Name,
ValueEnabled: ui.DefaultTheme().Focused.Options.EnabledValue,
ValueDisabled: ui.DefaultTheme().Focused.Options.DisabledValue,
SelectedName: ui.DefaultTheme().Focused.Options.SelectedName,
Cursor: ui.DefaultTheme().Focused.Cursor,
}
}
29 changes: 29 additions & 0 deletions cmd/slackdump/internal/ui/cfgui/keymap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cfgui

import "github.com/charmbracelet/bubbles/key"

type Keymap struct {
Up key.Binding
Down key.Binding
Home key.Binding
End key.Binding
Refresh key.Binding
Select key.Binding
Quit key.Binding
}

func DefaultKeymap() *Keymap {
return &Keymap{
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑", "up")),
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓", "down")),
Home: key.NewBinding(key.WithKeys("home"), key.WithHelp("home/end", "top/bottom")),
End: key.NewBinding(key.WithKeys("end")),
Refresh: key.NewBinding(key.WithKeys("f5", "ctrl+r"), key.WithHelp("f5", "refresh")),
Select: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "submit")),
Quit: key.NewBinding(key.WithKeys("q", "esc", "ctrl+c"), key.WithHelp("q", "quit")),
}
}

func (k *Keymap) Bindings() []key.Binding {
return []key.Binding{k.Up, k.Down, k.Home, k.Refresh, k.Select, k.Quit}
}
111 changes: 71 additions & 40 deletions cmd/slackdump/internal/ui/cfgui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"fmt"
"strings"

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui/updaters"
)

Expand All @@ -21,29 +21,37 @@ const (
notFound = -1
)

type Style struct {
Border lipgloss.Style
Title lipgloss.Style
Description lipgloss.Style
Name lipgloss.Style
ValueEnabled lipgloss.Style
ValueDisabled lipgloss.Style
SelectedName lipgloss.Style
Cursor lipgloss.Style
}

type configmodel struct {
finished bool
cfgFn func() Configuration
focused bool
cursor int
end int
Style Style
last int
state state
help help.Model

style *Style
keymap *Keymap

child tea.Model
state state
cfgFn func() Configuration
}

func NewConfigUI(sty *Style, cfgFn func() Configuration) tea.Model {
end := 0
for _, group := range cfgFn() {
end += len(group.Params)
}
end--
return &configmodel{
cfgFn: cfgFn,
last: end,
keymap: DefaultKeymap(),
style: sty,
help: help.New(),
}
}

func (m configmodel) Init() tea.Cmd {
func (m *configmodel) Init() tea.Cmd {
return nil
}

Expand All @@ -55,7 +63,11 @@ const (
inline
)

func (m configmodel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *configmodel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if !m.focused {
return m, nil
}

var cmds []tea.Cmd

if _, ok := msg.(updaters.WMClose); m.child != nil && !ok && m.state != selecting {
Expand All @@ -71,28 +83,28 @@ func (m configmodel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.child = nil
cmds = append(cmds, refreshCfgCmd)
case tea.KeyMsg:
switch msg.String() {
case "up", "k":
switch {
case key.Matches(msg, m.keymap.Up):
if m.cursor > 0 {
m.cursor--
} else {
// wrap around
m.cursor = m.end
m.cursor = m.last
}
case "down", "j":
if m.cursor < m.end {
case key.Matches(msg, m.keymap.Down):
if m.cursor < m.last {
m.cursor++
} else {
// wrap around
m.cursor = 0
}
case "home":
case key.Matches(msg, m.keymap.Home):
m.cursor = 0
case "end":
m.cursor = m.end
case "f5":
case key.Matches(msg, m.keymap.End):
m.cursor = m.last
case key.Matches(msg, m.keymap.Refresh):
cmds = append(cmds, refreshCfgCmd)
case "enter":
case key.Matches(msg, m.keymap.Select):
i, j := locateParam(m.cfgFn(), m.cursor)
if i == notFound || j == notFound {
return m, nil
Expand All @@ -106,7 +118,7 @@ func (m configmodel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.child = params.Updater
cmds = append(cmds, m.child.Init())
}
case "q", "esc", "ctrl+c":
case key.Matches(msg, m.keymap.Quit):
// child is active
if m.state != selecting {
break
Expand All @@ -119,41 +131,59 @@ func (m configmodel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Batch(cmds...)
}

func (m configmodel) View() string {
func (m *configmodel) SetFocus(b bool) {
m.focused = b
}

func (m *configmodel) IsFocused() bool {
return m.focused
}

func (m *configmodel) Reset() {
m.finished = false
m.state = selecting
m.child = nil
}

func (m *configmodel) View() string {
if m.finished {
return ""
}
var sty = m.style.Focused
if !m.focused {
sty = m.style.Blurred
}
if m.child != nil && len(m.child.View()) > 0 && m.state == editing {
return m.child.View()
}
return ui.DefaultTheme().Focused.Border.Render(m.view())
return sty.Border.Render(m.view(sty))
}

func (m configmodel) view() string {
func (m *configmodel) view(sty StyleSet) string {
var buf strings.Builder
line := 0
descr := ""
for i, group := range m.cfgFn() {
buf.WriteString(alignGroup + m.Style.Title.Render(group.Name))
buf.WriteString(alignGroup + sty.Title.Render(group.Name))
buf.WriteString("\n")
keyLen, valLen := group.maxLen()
for j, param := range group.Params {
selected := line == m.cursor
if selected {
buf.WriteString(m.Style.Cursor.Render(cursorChar))
buf.WriteString(sty.Cursor.Render(cursorChar))
descr = m.cfgFn()[i].Params[j].Description
} else {
buf.WriteString(" ")
}

valfmt := m.Style.ValueDisabled
valfmt := sty.ValueDisabled
if param.Updater != nil {
valfmt = m.Style.ValueEnabled
valfmt = sty.ValueEnabled
}

namefmt := m.Style.Name
namefmt := sty.Name
if selected {
namefmt = m.Style.SelectedName
namefmt = sty.SelectedName
}
fmt.Fprintf(&buf, alignParam+namefmt.Render(fmt.Sprintf("% *s", keyLen, param.Name))+" ")
if selected && m.state == inline {
Expand All @@ -164,7 +194,8 @@ func (m configmodel) view() string {
line++
}
}
buf.WriteString(alignGroup + m.Style.Description.Render(descr))
buf.WriteString(alignGroup + sty.Description.Render(descr) + "\n")
buf.WriteString(m.help.ShortHelpView(m.keymap.Bindings()))

return buf.String()
}
Expand Down
48 changes: 48 additions & 0 deletions cmd/slackdump/internal/ui/cfgui/style.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cfgui

import (
"github.com/charmbracelet/lipgloss"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/ui"
)

type Style struct {
Focused StyleSet
Blurred StyleSet
}

type StyleSet struct {
Border lipgloss.Style
Title lipgloss.Style
Description lipgloss.Style
Name lipgloss.Style
ValueEnabled lipgloss.Style
ValueDisabled lipgloss.Style
SelectedName lipgloss.Style
Cursor lipgloss.Style
}

func DefaultStyle() *Style {
t := ui.DefaultTheme()
return &Style{
Focused: StyleSet{
Border: t.Focused.Border,
Title: t.Focused.Options.Section,
Description: t.Focused.Description,
Name: t.Focused.Options.Name,
ValueEnabled: t.Focused.Options.EnabledValue,
ValueDisabled: t.Focused.Options.DisabledValue,
SelectedName: t.Focused.Options.SelectedName,
Cursor: t.Focused.Cursor,
},
Blurred: StyleSet{
Border: t.Blurred.Border,
Title: t.Blurred.Options.Section,
Description: t.Blurred.Description,
Name: t.Blurred.Options.Name,
ValueEnabled: t.Blurred.Options.EnabledValue,
ValueDisabled: t.Blurred.Options.DisabledValue,
SelectedName: t.Blurred.Options.SelectedName,
Cursor: t.Blurred.Cursor,
},
}
}
11 changes: 11 additions & 0 deletions cmd/slackdump/internal/ui/dumpui/focusmodel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dumpui

import tea "github.com/charmbracelet/bubbletea"

type FocusModel interface {
tea.Model
SetFocus(bool)
IsFocused() bool
// Reset should reset the model to its initial state.
Reset()
}
23 changes: 23 additions & 0 deletions cmd/slackdump/internal/ui/dumpui/keymap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dumpui

import "github.com/charmbracelet/bubbles/key"

type Keymap struct {
Up key.Binding
Down key.Binding
Select key.Binding
Quit key.Binding
}

func DefaultKeymap() *Keymap {
return &Keymap{
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑", "up")),
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓", "down")),
Select: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "submit")),
Quit: key.NewBinding(key.WithKeys("q", "ctrl+c"), key.WithHelp("q", "quit")),
}
}

func (k *Keymap) Bindings() []key.Binding {
return []key.Binding{k.Up, k.Down, k.Select, k.Quit}
}
Loading

0 comments on commit 8cf2227

Please sign in to comment.