Allow for README templates

Toby Padilla created

Change summary

git/git.go                         | 17 ++---
tui/bubble.go                      | 53 +++++--------------
tui/bubbles/repo/bubble.go         | 85 +++++++++++++++++++++++++++++--
tui/bubbles/repo/viewport_patch.go |  2 
tui/commands.go                    | 20 +++----
tui/defaults.go                    |  3 
6 files changed, 113 insertions(+), 67 deletions(-)

Detailed changes

git/git.go 🔗

@@ -28,8 +28,6 @@ type RepoCommit struct {
 
 type CommitLog []RepoCommit
 
-type ReadmeTransform func(string) string
-
 func (cl CommitLog) Len() int      { return len(cl) }
 func (cl CommitLog) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
 func (cl CommitLog) Less(i, j int) bool {
@@ -37,19 +35,18 @@ func (cl CommitLog) Less(i, j int) bool {
 }
 
 type RepoSource struct {
-	Path            string
-	mtx             sync.Mutex
-	repos           []*Repo
-	commits         CommitLog
-	readmeTransform ReadmeTransform
+	Path    string
+	mtx     sync.Mutex
+	repos   []*Repo
+	commits CommitLog
 }
 
-func NewRepoSource(repoPath string, rf ReadmeTransform) *RepoSource {
+func NewRepoSource(repoPath string) *RepoSource {
 	err := os.MkdirAll(repoPath, os.ModeDir|os.FileMode(0700))
 	if err != nil {
 		log.Fatal(err)
 	}
-	rs := &RepoSource{Path: repoPath, readmeTransform: rf}
+	rs := &RepoSource{Path: repoPath}
 	return rs
 }
 
@@ -129,7 +126,7 @@ func (rs *RepoSource) loadRepo(name string, rg *git.Repository) (*Repo, error) {
 			if err == nil {
 				rmd, err := rf.Contents()
 				if err == nil {
-					r.Readme = rs.readmeTransform(rmd)
+					r.Readme = rmd
 				}
 			}
 		}

tui/bubble.go 🔗

@@ -9,9 +9,7 @@ import (
 	"smoothie/tui/bubbles/selection"
 	"time"
 
-	"github.com/charmbracelet/bubbles/viewport"
 	tea "github.com/charmbracelet/bubbletea"
-	"github.com/charmbracelet/glamour"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/gliderlabs/ssh"
 )
@@ -28,6 +26,7 @@ const (
 
 type MenuEntry struct {
 	Name string `json:"name"`
+	Note string `json:"note"`
 	Repo string `json:"repo"`
 }
 
@@ -45,20 +44,19 @@ type SessionConfig struct {
 }
 
 type Bubble struct {
-	config         *Config
-	state          sessionState
-	error          string
-	width          int
-	height         int
-	windowChanges  <-chan ssh.Window
-	repoSource     *git.RepoSource
-	repoMenu       []MenuEntry
-	repos          []*git.Repo
-	boxes          []tea.Model
-	activeBox      int
-	repoSelect     *selection.Bubble
-	commitsLog     *commits.Bubble
-	readmeViewport *ViewportBubble
+	config        *Config
+	state         sessionState
+	error         string
+	width         int
+	height        int
+	windowChanges <-chan ssh.Window
+	repoSource    *git.RepoSource
+	repoMenu      []MenuEntry
+	repos         []*git.Repo
+	boxes         []tea.Model
+	activeBox     int
+	repoSelect    *selection.Bubble
+	commitsLog    *commits.Bubble
 }
 
 func NewBubble(cfg *Config, sCfg *SessionConfig) *Bubble {
@@ -69,12 +67,6 @@ func NewBubble(cfg *Config, sCfg *SessionConfig) *Bubble {
 		windowChanges: sCfg.WindowChanges,
 		repoSource:    cfg.RepoSource,
 		boxes:         make([]tea.Model, 2),
-		readmeViewport: &ViewportBubble{
-			Viewport: &viewport.Model{
-				Width:  boxRightWidth - horizontalPadding - 2,
-				Height: sCfg.Height - verticalPadding - viewportHeightConstant,
-			},
-		},
 	}
 	b.state = startState
 	return b
@@ -149,21 +141,6 @@ func (b *Bubble) View() string {
 	return appBoxStyle.Render(content)
 }
 
-func glamourReadme(md string) string {
-	tr, err := glamour.NewTermRenderer(
-		glamour.WithAutoStyle(),
-		glamour.WithWordWrap(boxRightWidth-2),
-	)
-	if err != nil {
-		log.Fatal(err)
-	}
-	mdt, err := tr.Render(md)
-	if err != nil {
-		return md
-	}
-	return mdt
-}
-
 func loadConfig(rs *git.RepoSource) (*Config, error) {
 	cfg := &Config{}
 	cfg.RepoSource = rs
@@ -183,7 +160,7 @@ func loadConfig(rs *git.RepoSource) (*Config, error) {
 }
 
 func SessionHandler(reposPath string, repoPoll time.Duration) func(ssh.Session) (tea.Model, []tea.ProgramOption) {
-	rs := git.NewRepoSource(reposPath, glamourReadme)
+	rs := git.NewRepoSource(reposPath)
 	err := createDefaultConfigRepo(rs)
 	if err != nil {
 		if err != nil {

tui/bubbles/repo/bubble.go 🔗

@@ -1,22 +1,95 @@
 package repo
 
 import (
+	"bytes"
+	"smoothie/git"
+	"text/template"
+
+	"github.com/charmbracelet/bubbles/viewport"
 	tea "github.com/charmbracelet/bubbletea"
-	"github.com/go-git/go-git/v5"
+	"github.com/charmbracelet/glamour"
 )
 
+type ErrMsg struct {
+	Error error
+}
+
 type Bubble struct {
-	repo *git.Repository
+	templateObject interface{}
+	repoSource     *git.RepoSource
+	name           string
+	repo           *git.Repo
+	readmeViewport *ViewportBubble
+}
+
+func NewBubble(rs *git.RepoSource, name string, width int, height int, tmp interface{}) *Bubble {
+	return &Bubble{
+		templateObject: tmp,
+		repoSource:     rs,
+		name:           name,
+		readmeViewport: &ViewportBubble{
+			Viewport: &viewport.Model{
+				Width:  width,
+				Height: height,
+			},
+		},
+	}
 }
 
 func (b *Bubble) Init() tea.Cmd {
-	return nil
+	return b.setupCmd
 }
 
-func (b *Bubble) Update(tea.Msg) (tea.Model, tea.Cmd) {
-	return nil, nil
+func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+	return b.readmeViewport.Update(msg)
 }
 
 func (b *Bubble) View() string {
-	return "repo"
+	return b.readmeViewport.View()
+}
+
+func (b *Bubble) setupCmd() tea.Msg {
+	r, err := b.repoSource.GetRepo(b.name)
+	if err != nil {
+		return ErrMsg{err}
+	}
+	md := r.Readme
+	if b.templateObject != nil {
+		t, err := template.New("readme").Parse(md)
+		if err != nil {
+			return ErrMsg{err}
+		}
+		buf := &bytes.Buffer{}
+		err = t.Execute(buf, b.templateObject)
+		if err != nil {
+			return ErrMsg{err}
+		}
+		md = buf.String()
+	}
+	md, err = b.glamourize(md)
+	if err != nil {
+		return ErrMsg{err}
+	}
+	b.readmeViewport.Viewport.GotoTop()
+	b.readmeViewport.Viewport.SetContent(md)
+	return nil
+}
+
+func (b *Bubble) templatize(t string) (string, error) {
+	return "", nil
+}
+
+func (b *Bubble) glamourize(md string) (string, error) {
+	tr, err := glamour.NewTermRenderer(
+		glamour.WithAutoStyle(),
+		glamour.WithWordWrap(b.readmeViewport.Viewport.Width),
+	)
+	if err != nil {
+		return "", err
+	}
+	mdt, err := tr.Render(md)
+	if err != nil {
+		return "", err
+	}
+	return mdt, nil
 }

tui/commands.go 🔗

@@ -2,6 +2,7 @@ package tui
 
 import (
 	"smoothie/tui/bubbles/commits"
+	"smoothie/tui/bubbles/repo"
 	"smoothie/tui/bubbles/selection"
 
 	tea "github.com/charmbracelet/bubbletea"
@@ -57,16 +58,13 @@ func (b *Bubble) loadGitCmd() tea.Msg {
 }
 
 func (b *Bubble) getRepoCmd(name string) tea.Cmd {
-	return func() tea.Msg {
-		r, err := b.repoSource.GetRepo(name)
-		if err != nil {
-			return errMsg{err}
-		}
-		b.readmeViewport.Viewport.GotoTop()
-		b.readmeViewport.Viewport.Height = b.height - verticalPadding - viewportHeightConstant
-		b.readmeViewport.Viewport.Width = boxLeftWidth - 2
-		b.readmeViewport.Viewport.SetContent(r.Readme)
-		b.boxes[1] = b.readmeViewport
-		return nil
+	var tmplConfig *Config
+	if name == "config" {
+		tmplConfig = b.config
 	}
+	h := b.height - verticalPadding - viewportHeightConstant
+	w := boxRightWidth - 2
+	rb := repo.NewBubble(b.repoSource, name, w, h, tmplConfig)
+	b.boxes[1] = rb
+	return rb.Init()
 }

tui/defaults.go 🔗

@@ -17,7 +17,8 @@ const defaultConfig = `{
 	"menu": [
 	  {
 			"name": "Home",
-			"repo": "config"
+			"repo": "config",
+			"note": "Configuration and content repo"
 		}
 	]
 }`