package export import ( _ "embed" "encoding/json" "html/template" "io" "strings" "time" "github.com/lerko/nib/internal/db" "github.com/lerko/nib/internal/display" ) //go:embed template.html var templateHTML string type TemplateSlot struct { Name string `json:"name"` Default string `json:"default"` } type CheckStep struct { Text string `json:"text"` Done bool `json:"done"` } type DecisionData struct { Chose string `json:"chose"` Why string `json:"why"` Rejected []string `json:"rejected"` } type CardView struct { ID string Glyph string CardType string Title string Body template.HTML Description template.HTML SearchText string Pinned bool Tags []string UseCount int LinkURL string Slots []TemplateSlot TemplateBody string Steps []CheckStep Progress int Decision DecisionData } type DeckView struct { Title string Count int ExportedAt string Types []string Cards []CardView } func RenderHTML(w io.Writer, entities []*db.Entity, title string) error { tmpl, err := template.New("deck").Parse(templateHTML) if err != nil { return err } seen := map[string]bool{} var types []string var cards []CardView for _, e := range entities { if e.CardType == nil { continue } ct := string(*e.CardType) if !seen[ct] { seen[ct] = true types = append(types, ct) } cards = append(cards, buildCardView(e)) } data := DeckView{ Title: title, Count: len(cards), ExportedAt: time.Now().Format("Jan 2, 2006"), Types: types, Cards: cards, } return tmpl.Execute(w, data) } func buildCardView(e *db.Entity) CardView { ct := "" if e.CardType != nil { ct = string(*e.CardType) } title := "" if e.Title != nil { title = *e.Title } body := e.Body desc := "" if e.Description != nil { desc = *e.Description } search := strings.ToLower(title + " " + body + " " + desc + " " + strings.Join(e.Tags, " ")) cv := CardView{ ID: e.ID, Glyph: display.DisplayGlyph(e.Glyph, e.CardType), CardType: ct, Title: title, Body: template.HTML(template.HTMLEscapeString(body)), SearchText: search, Pinned: e.Pinned, Tags: e.Tags, UseCount: e.UseCount, } if desc != "" { cv.Description = template.HTML(template.HTMLEscapeString(desc)) } if e.CardData != nil { parseCardData(&cv, ct, *e.CardData, body) } return cv } func parseCardData(cv *CardView, ct string, raw string, body string) { switch ct { case "snippet": // body is the snippet content, already set case "template": var data struct { Slots []TemplateSlot `json:"slots"` } if json.Unmarshal([]byte(raw), &data) == nil { cv.Slots = data.Slots } cv.TemplateBody = body case "checklist": var data struct { Steps []CheckStep `json:"steps"` } if json.Unmarshal([]byte(raw), &data) == nil { cv.Steps = data.Steps done := 0 for _, s := range cv.Steps { if s.Done { done++ } } if len(cv.Steps) > 0 { cv.Progress = (done * 100) / len(cv.Steps) } } case "decision": var dec DecisionData if json.Unmarshal([]byte(raw), &dec) == nil { cv.Decision = dec } case "link": var data struct { URL string `json:"url"` } if json.Unmarshal([]byte(raw), &data) == nil { cv.LinkURL = data.URL } } }