From b34c2073cd97d64492ca7c38d3548f366e3e1492 Mon Sep 17 00:00:00 2001 From: Aaron Martell Date: Mon, 13 Jul 2026 16:07:10 -0500 Subject: [PATCH 1/2] Added missing doc strings --- cmd/book/main.go | 3 ++ cmd/book/mark.go | 6 +-- internal/book/templates.go | 34 ++++++++++---- internal/book/types.go | 74 +++++++++++++++++++++--------- internal/catalog/catalog.go | 4 ++ internal/catalog/toml.go | 6 ++- internal/catalog/toml_test.go | 2 +- internal/model/collection_model.go | 2 +- internal/model/mark_model.go | 7 +-- internal/model/shelf_model.go | 2 +- internal/model/tea.go | 6 +++ internal/theme/theme.go | 12 +++++ internal/web/web.go | 5 ++ main.go | 1 + 14 files changed, 123 insertions(+), 41 deletions(-) diff --git a/cmd/book/main.go b/cmd/book/main.go index 5240cb0..b411ba8 100644 --- a/cmd/book/main.go +++ b/cmd/book/main.go @@ -1,3 +1,4 @@ +// Package cmd implements the book CLI. package cmd import ( @@ -23,10 +24,12 @@ var ( version = "internal" ) +// SetVersion sets the application version string used by the CLI. func SetVersion(v string) { version = v } +// Main builds and runs the book CLI application. func Main() { var confirm bool var interactive bool diff --git a/cmd/book/mark.go b/cmd/book/mark.go index bcab074..138451c 100644 --- a/cmd/book/mark.go +++ b/cmd/book/mark.go @@ -52,8 +52,8 @@ func addMark(bs *book.BookShelves, URL string, tags string, shelfName string, co } mark := book.Mark{ - Id: id, - Url: URL, + ID: id, + URL: URL, Tags: strings.Split(tags, ","), } @@ -61,7 +61,7 @@ func addMark(bs *book.BookShelves, URL string, tags string, shelfName string, co if title != "" { mark.Name = title } else { - fetchedTitle, err := web.LoadWebsite(mark.Url) + fetchedTitle, err := web.LoadWebsite(mark.URL) if err != nil { return err } diff --git a/internal/book/templates.go b/internal/book/templates.go index c0ad55f..b4a6ac4 100644 --- a/internal/book/templates.go +++ b/internal/book/templates.go @@ -1,27 +1,39 @@ package book +// Templatable defines the view-model interface used to render TUI success screens. type Templatable interface { Primary() string Secondary() string List() []string } -// Template interface methods -func (s *BookShelves) Primary() string { return "" } -func (s *BookShelves) Secondary() string { return "" } -func (s *BookShelves) List() []string { return s.ShelfNames() } +// Primary returns an empty string for BookShelves. +func (bs *BookShelves) Primary() string { return "" } +// Secondary returns an empty string for BookShelves. +func (bs *BookShelves) Secondary() string { return "" } +// List returns the names of all shelves. +func (bs *BookShelves) List() []string { return bs.ShelfNames() } +// Primary returns the shelf name. func (s *Shelf) Primary() string { return s.Name } +// Secondary returns an empty string for Shelf. func (s *Shelf) Secondary() string { return "" } +// List returns the names of all collections in the shelf. func (s *Shelf) List() []string { return s.CollectionsNames() } -func (s *Collection) Primary() string { return s.Shelf.Name } -func (s *Collection) Secondary() string { return s.Name } -func (s *Collection) List() []string { return s.MarksNames() } +// Primary returns the parent shelf name. +func (c *Collection) Primary() string { return c.Shelf.Name } +// Secondary returns the collection name. +func (c *Collection) Secondary() string { return c.Name } +// List returns the names of all marks in the collection. +func (c *Collection) List() []string { return c.MarksNames() } -func (s *Mark) Primary() string { return s.Name } -func (s *Mark) Secondary() string { return s.Url } -func (s *Mark) List() []string { return s.Tags } +// Primary returns the mark title. +func (m *Mark) Primary() string { return m.Name } +// Secondary returns the mark URL. +func (m *Mark) Secondary() string { return m.URL } +// List returns the mark's tags. +func (m *Mark) List() []string { return m.Tags } // ViewTemplate used to templatize TUI success screens type ViewTemplate struct { @@ -61,6 +73,7 @@ func markTemplate(primary string) ViewTemplate { } } +// DefaultViewTemplates provides the built-in TUI success-screen templates. var DefaultViewTemplates = map[string]ViewTemplate{ "shelf-list": {ListTitle: "You've knocked over all your shelves:"}, "shelf-add": {PrimaryTitle: "You added shelf:", SecondaryTitle: "Along chosen collection:"}, @@ -76,6 +89,7 @@ var DefaultViewTemplates = map[string]ViewTemplate{ "mark-delete": markTemplate("To delete mark:"), } +// UserViewTemplates holds user-defined template overrides loaded at runtime. var UserViewTemplates = map[string]ViewTemplate{} // GetTemplate merges user overrides over the default for a given key. diff --git a/internal/book/types.go b/internal/book/types.go index 2370155..0fb94a0 100644 --- a/internal/book/types.go +++ b/internal/book/types.go @@ -1,3 +1,4 @@ +// Package book data models, catalog theme and templates, and their methods package book import ( @@ -17,10 +18,12 @@ import ( const errorBullet string = "󰯷" // "nf-md-alpha_e_box_outline +// TOMLFile defines exportable TOML files type TOMLFile interface { FileDetail() string } +// Config holds internal application configuration settings, including loaded files type Config struct { CatalogFormat string `toml:"catalog_format"` ShelfRoot string `toml:"shelf_directory"` @@ -33,27 +36,29 @@ type Config struct { Templates map[string]ViewTemplate `toml:"-"` // loaded at run time } -func (c *Config) LoadTheme(interactive bool) error { - c.Theme = theme.NewTheme(&theme.ThemeConfig{}) +// LoadTheme loads the theme file or falls back to defaults. +func (cfg *Config) LoadTheme(interactive bool) error { + cfg.Theme = theme.NewTheme(&theme.ThemeConfig{}) // load theme file or use defaults for TUI/interactive features if interactive { - raw, err := theme.LoadThemeConfig(c.ThemeFile) + raw, err := theme.LoadThemeConfig(cfg.ThemeFile) if err != nil { return err } - c.Theme = theme.NewTheme(raw) // nil raw = defaults + cfg.Theme = theme.NewTheme(raw) // nil raw = defaults } return nil } -func (c *Config) LoadTemplates() error { - c.Templates = make(map[string]ViewTemplate) +// LoadTemplates loads user template overrides on top of built-in defaults. +func (cfg *Config) LoadTemplates() error { + cfg.Templates = make(map[string]ViewTemplate) // Start with defaults - maps.Copy(c.Templates, DefaultViewTemplates) + maps.Copy(cfg.Templates, DefaultViewTemplates) - data, err := os.ReadFile(c.TemplateFile) + data, err := os.ReadFile(cfg.TemplateFile) if os.IsNotExist(err) { // Not an error — user hasn't customized, defaults are fine return nil @@ -69,7 +74,7 @@ func (c *Config) LoadTemplates() error { // Overlay user partials onto defaults for k, user := range userTmpls { - base, ok := c.Templates[k] + base, ok := cfg.Templates[k] if !ok { // Unknown key — skip or warn continue @@ -83,21 +88,23 @@ func (c *Config) LoadTemplates() error { if user.ListTitle != "" { base.ListTitle = user.ListTitle } - c.Templates[k] = base + cfg.Templates[k] = base } return nil } -func (c *Config) StyledError(e error) string { - if !c.Interactive { +// StyledError returns a styled error string for interactive mode, or plain text otherwise. +func (cfg *Config) StyledError(e error) string { + if !cfg.Interactive { return e.Error() } // return styled error only in interactive mode - return c.Theme.Style("highlight").Render("HEAVENS TO MURGATROYD!") + "\n" + - c.Theme.Style("error").Render(errorBullet, e.Error()) + return cfg.Theme.Style("highlight").Render("HEAVENS TO MURGATROYD!") + "\n" + + cfg.Theme.Style("error").Render(errorBullet, e.Error()) } +// FileConfig holds externally writable application configuration settings type FileConfig struct { CatalogFormat string `toml:"catalog_format"` ShelfRoot string `toml:"shelf_directory"` @@ -108,18 +115,22 @@ type FileConfig struct { ConfigFile string `toml:"-"` // path to config file, typical BOOK_CONFIG } +// FileDetail returns the file path used to create the config TOML file. func (f *FileConfig) FileDetail() string { return f.ConfigFile } +// BookShelves is the top-level container for all shelf data. type BookShelves []Shelf +// AddShelf appends a new shelf to the collection. func (bs *BookShelves) AddShelf(shelf Shelf) { // Dereference bs (*bs) to get the slice, append, // and reassign the result to the dereferenced pointer *bs = append(*bs, shelf) } +// Shelf returns a shelf by name, or a zero-value Shelf if not found. func (bs *BookShelves) Shelf(s string) *Shelf { for i := range *bs { if (*bs)[i].Name == s { @@ -129,6 +140,7 @@ func (bs *BookShelves) Shelf(s string) *Shelf { return &Shelf{} } +// ShelfNames returns the names of all loaded shelves. func (bs *BookShelves) ShelfNames() []string { shelvesNames := make([]string, 0, len(*bs)) for _, p := range *bs { @@ -137,6 +149,7 @@ func (bs *BookShelves) ShelfNames() []string { return shelvesNames } +// LoadParents sets back-pointers from marks to their parent shelf and collection. func (bs *BookShelves) LoadParents() { // Loads Shelf and Collection pointers in Marks for i := range *bs { @@ -151,13 +164,13 @@ func (bs *BookShelves) LoadParents() { } } +// VerifyUniqueURL returns an error if the given ID already exists in any mark. func (bs *BookShelves) VerifyUniqueURL(id string) error { for _, b := range *bs { for _, c := range b.Collections { for _, m := range c.Marks { - if m.Id == id { - errMessage := fmt.Sprintf("duplicate URL Found!\n\n%s", m.FullDetail()) - return fmt.Errorf("%s", errMessage) + if m.ID == id { + return fmt.Errorf("duplicate URL Found!\n\n%s", m.FullDetail()) } } } @@ -165,6 +178,7 @@ func (bs *BookShelves) VerifyUniqueURL(id string) error { return nil } +// Shelf is a named container for collections stored in a single TOML file. type Shelf struct { Name string `toml:"shelf_name" json:"shelf_name"` Description string `toml:"shelf_desc,omitempty" json:"shelf_desc,omitempty"` @@ -172,6 +186,7 @@ type Shelf struct { FilePath string `toml:"-" json:"-"` } +// AddFileDetail sets the on-disk file path for the shelf based on its name. func (s *Shelf) AddFileDetail(c *Config) { formattedName := strings.ReplaceAll(strings.TrimSpace(s.Name), " ", "_") fileName := fmt.Sprintf("%s.%s", formattedName, c.CatalogFormat) @@ -179,14 +194,17 @@ func (s *Shelf) AddFileDetail(c *Config) { s.FilePath = filepath.Join(c.ShelfRoot, fileName) } +// FileDetail returns the file path of the shelf's TOML file. func (s *Shelf) FileDetail() string { return s.FilePath } +// Collection returns a collection by name from the shelf. func (s *Shelf) Collection(c string) *Collection { return s.Collections[c] } +// AddCollection registers a collection in the shelf. func (s *Shelf) AddCollection(c *Collection) { if s.Collections == nil { s.Collections = make(map[string]*Collection) @@ -194,6 +212,7 @@ func (s *Shelf) AddCollection(c *Collection) { s.Collections[c.Name] = c } +// CollectionsNames returns the names of all collections in the shelf, sorted. func (s *Shelf) CollectionsNames() []string { names := make([]string, 0, len(s.Collections)) for name := range s.Collections { @@ -203,6 +222,7 @@ func (s *Shelf) CollectionsNames() []string { return names } +// Collection is a named grouping of bookmarks within a shelf. type Collection struct { Shelf *Shelf `toml:"-" json:"-"` Name string `toml:"collection_name" json:"collection_name"` @@ -210,6 +230,7 @@ type Collection struct { Marks []*Mark `toml:"marks" json:"marks"` } +// MarksNames returns the names of all marks in the collection. func (c *Collection) MarksNames() []string { markNames := make([]string, 0, len(c.Marks)) for _, m := range c.Marks { @@ -218,6 +239,7 @@ func (c *Collection) MarksNames() []string { return markNames } +// AllTags returns every tag across all marks in the collection, sorted and deduplicated. func (c *Collection) AllTags() []string { var tags []string for _, m := range c.Marks { @@ -227,6 +249,7 @@ func (c *Collection) AllTags() []string { return tags } +// Mark returns a mark by name from the collection. func (c *Collection) Mark(m string) *Mark { for _, n := range c.Marks { if n.Name == m { @@ -236,34 +259,40 @@ func (c *Collection) Mark(m string) *Mark { return nil } +// AddMark appends a mark to the collection. func (c *Collection) AddMark(m *Mark) { c.Marks = append(c.Marks, m) } +// DeleteMark removes the given mark from the collection. func (c *Collection) DeleteMark(m *Mark) { c.Marks = slices.DeleteFunc(c.Marks, func(d *Mark) bool { return d == m }) } +// Mark is a single bookmark with a title, URL, tags, and back-references. type Mark struct { Shelf *Shelf `toml:"-" json:"-"` Collection *Collection `toml:"-" json:"-"` - Id string `toml:"catalog_id" json:"catalog_id"` + ID string `toml:"catalog_id" json:"catalog_id"` Name string `toml:"title" json:"title"` - Url string `toml:"url" json:"url"` + URL string `toml:"url" json:"url"` Tags []string `toml:"tags" json:"tags"` } +// Description returns a human-readable summary of the mark. func (m *Mark) Description() string { - return fmt.Sprintf("Title: %s\nURL: %s\nTags: %s", m.Name, m.Url, strings.Join(m.Tags, ",")) + return fmt.Sprintf("Title: %s\nURL: %s\nTags: %s", m.Name, m.URL, strings.Join(m.Tags, ",")) } +// FullDetail returns a verbose summary including shelf, collection, title, URL, and tags. func (m *Mark) FullDetail() string { - return fmt.Sprintf("Shelf: %s\nCollection: %s\nTitle: %s\nURL: %s\nTags: %s", m.Shelf.Name, m.Collection.Name, m.Name, m.Url, strings.Join(m.Tags, ",")) + return fmt.Sprintf("Shelf: %s\nCollection: %s\nTitle: %s\nURL: %s\nTags: %s", m.Shelf.Name, m.Collection.Name, m.Name, m.URL, strings.Join(m.Tags, ",")) } +// DedupUnique concatenates and deduplicates multiple slices while preserving first-seen order. func DedupUnique[T comparable](slice ...[]T) []T { merged := slices.Concat(slice...) seen := make(map[T]struct{}, len(merged)) @@ -277,6 +306,7 @@ func DedupUnique[T comparable](slice ...[]T) []T { return unique } +// StructIsEmpty reports whether the given struct pointer is nil or contains only zero values. func StructIsEmpty[T any](ptr *T) bool { if ptr == nil { return true @@ -289,6 +319,7 @@ func StructIsEmpty[T any](ptr *T) bool { return val.IsZero() } +// GenerateID returns the first 8 hex characters of the SHA-256 hash of a URL. func GenerateID(url string) string { hash := sha256.Sum256([]byte(url)) // Return the first 8 characters of the hex representation @@ -303,6 +334,7 @@ func MergeTags(sources ...[]string) []string { return slices.DeleteFunc(merged, func(e string) bool { return e == "" }) } +// PrintCatalog serializes an item as JSON or TOML to stdout. func PrintCatalog[T any](item T, format string) error { switch format { case "json": diff --git a/internal/catalog/catalog.go b/internal/catalog/catalog.go index bb9439d..e80c930 100644 --- a/internal/catalog/catalog.go +++ b/internal/catalog/catalog.go @@ -1,3 +1,5 @@ +// Package catalog handles loading of shelf files and creating/writing of toml +// and json files package catalog import ( @@ -12,6 +14,7 @@ import ( "github.com/polymorcodeus/book/internal/book" ) +// LoadShelves reads all shelf TOML files from disk into the given BookShelves. func LoadShelves(bs *book.BookShelves, config *book.Config) error { globDir := fmt.Sprintf("%s/*.%s", config.ShelfRoot, config.CatalogFormat) files, err := filepath.Glob(globDir) @@ -35,6 +38,7 @@ func LoadShelves(bs *book.BookShelves, config *book.Config) error { return nil } +// LoadCatalog loads shelves with an optional spinner when running interactively. func LoadCatalog(bs *book.BookShelves, config *book.Config, interactive bool) error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() diff --git a/internal/catalog/toml.go b/internal/catalog/toml.go index 1d4bf2a..e7d8750 100644 --- a/internal/catalog/toml.go +++ b/internal/catalog/toml.go @@ -14,6 +14,7 @@ import ( "github.com/polymorcodeus/book/internal/theme" ) +// VerifyExists reports whether a file or directory exists at the given path. func VerifyExists(filename string) (bool, error) { _, err := os.Stat(filename) if err == nil { @@ -26,6 +27,7 @@ func VerifyExists(filename string) (bool, error) { return false, err // remaining errors } +// CreateTOML atomically writes a TOML file for the given TOMLFile. func CreateTOML(t book.TOMLFile) (err error) { path := t.FileDetail() @@ -59,10 +61,12 @@ func CreateTOML(t book.TOMLFile) (err error) { return os.Rename(tmpPath, writePath) } +// UpdateShelfFile persists the given shelf to its on-disk TOML file. func UpdateShelfFile(s *book.Shelf) error { return CreateTOML(s) } +// EnsureConfig creates the config file if it does not already exist. func EnsureConfig(c *book.Config) error { exists, err := VerifyExists(c.ConfigFile) if err != nil { @@ -204,7 +208,7 @@ func resolveWritePath(path string) (string, error) { return filepath.Join(realDir, filepath.Base(current)), nil } -// DumpDefaultTheme serialises the built-in defaults as indented JSON. +// DumpDefaults serialises the built-in theme or template defaults as indented JSON. func DumpDefaults(config *book.Config, dump string) (err error) { var jsonData []byte var ( diff --git a/internal/catalog/toml_test.go b/internal/catalog/toml_test.go index 92bb8c8..69c57e6 100644 --- a/internal/catalog/toml_test.go +++ b/internal/catalog/toml_test.go @@ -116,7 +116,7 @@ func TestCreateTOMLAtomic(t *testing.T) { Marks: []*book.Mark{ { Name: "Example", - Url: "https://example.com", + URL: "https://example.com", Tags: []string{"demo"}, }, }, diff --git a/internal/model/collection_model.go b/internal/model/collection_model.go index 169e8a5..9bbb041 100644 --- a/internal/model/collection_model.go +++ b/internal/model/collection_model.go @@ -123,7 +123,7 @@ func (m getCollectionModel) View() tea.View { return tea.NewView(s.Base.Render(header + "\n" + body + "\n\n" + footer)) } -// To be used for editing descriptions/names in future +// GetCollectionForm to be used for editing descriptions/names in future func GetCollectionForm(bs *book.BookShelves, config *book.Config, action string) getCollectionModel { m := collectionModel{book: &Book{width: maxWidth}} m.book.styles = NewStyles(config) diff --git a/internal/model/mark_model.go b/internal/model/mark_model.go index 1e2e2eb..9ab35e2 100644 --- a/internal/model/mark_model.go +++ b/internal/model/mark_model.go @@ -145,7 +145,7 @@ func (m getMarkModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return editScreen, editScreen.Init() case "get": cmd = func() tea.Msg { - if err := web.OpenURL(m.get.mark.Url); err != nil { + if err := web.OpenURL(m.get.mark.URL); err != nil { return errMsg{err} } return nil @@ -202,7 +202,7 @@ func (m getMarkModel) View() tea.View { if m.get.verifyMark() { // Need to load selected mark for display m.get.reloadMarkModel() - displayMark = m.get.mark.Name + "\n\n" + m.get.mark.Url + "\n\n" + lipglossList(s.None, m.get.mark.Tags) + "\n" + displayMark = m.get.mark.Name + "\n\n" + m.get.mark.URL + "\n\n" + lipglossList(s.None, m.get.mark.Tags) + "\n" } } } @@ -236,6 +236,7 @@ func (m getMarkModel) View() tea.View { return tea.NewView(s.Base.Render(header + "\n" + body + "\n\n" + footer)) } +// GetMarkForm returns a TUI model for navigating shelves, collections, and marks. func GetMarkForm(bs *book.BookShelves, mark *book.Mark, config *book.Config, action string) getMarkModel { m := markModel{book: &Book{width: maxWidth}} m.book.styles = NewStyles(config) @@ -401,7 +402,7 @@ func (m editMarkModel) View() tea.View { currentCollection = s.StatusHeader.Render("Picked Collection") + "\n" + m.editor.mark.Collection.Name + "\n\n" currentMark = s.StatusHeader.Render("Editing Mark") + "\n" + m.editor.mark.Name - currentMark += "\n\n" + m.editor.mark.Url + "\n\n" + lipglossList(s.None, m.editor.mark.Tags) + "\n" + currentMark += "\n\n" + m.editor.mark.URL + "\n\n" + lipglossList(s.None, m.editor.mark.Tags) + "\n" const statusWidth = 68 statusMarginLeft := m.editor.book.width - statusWidth - lipgloss.Width(form) - s.Status.GetMarginRight() diff --git a/internal/model/shelf_model.go b/internal/model/shelf_model.go index 686e940..c99f119 100644 --- a/internal/model/shelf_model.go +++ b/internal/model/shelf_model.go @@ -121,7 +121,7 @@ func (m getShelfModel) View() tea.View { return tea.NewView(s.Base.Render(header + "\n" + body + "\n\n" + footer)) } -// To be used for editing descriptions/names in future +// GetShelfForm to be used for editing descriptions/names in future func GetShelfForm(bs *book.BookShelves, config *book.Config, action string) getShelfModel { m := shelfModel{book: &Book{width: maxWidth}} m.book.styles = NewStyles(config) diff --git a/internal/model/tea.go b/internal/model/tea.go index 0b5cfc7..d12a25a 100644 --- a/internal/model/tea.go +++ b/internal/model/tea.go @@ -1,3 +1,4 @@ +// Package model handles all bubble TUI components package model import ( @@ -12,6 +13,7 @@ import ( const maxWidth = 120 +// Styles holds the pre-built lipgloss styles for the TUI. type Styles struct { Base, HeaderText, @@ -29,6 +31,7 @@ type Styles struct { Form lipgloss.Style } +// NewStyles builds a Styles instance from the current theme configuration. func NewStyles(config *book.Config) *Styles { s := Styles{} s.Base = lipgloss.NewStyle(). @@ -67,6 +70,7 @@ type errMsg struct{ error } type shelfSavedMsg struct{} +// Book is the shared TUI state container used by all screen models. type Book struct { err error styles *Styles @@ -104,6 +108,7 @@ func (b Book) appErrorBoundaryView(text string) string { ) } +// RootScreen wraps a tea.Model and delegates the BubbleTea lifecycle to it. type RootScreen struct { Model tea.Model } @@ -121,6 +126,7 @@ func (m RootScreen) View() tea.View { return m.Model.View() } +// SwitchScreen replaces the wrapped model and re-initializes it. func (m RootScreen) SwitchScreen(model tea.Model) (tea.Model, tea.Cmd) { m.Model = model return m.Model, m.Model.Init() diff --git a/internal/theme/theme.go b/internal/theme/theme.go index 63ff187..fd3af35 100644 --- a/internal/theme/theme.go +++ b/internal/theme/theme.go @@ -1,3 +1,5 @@ +// Package theme skins bubbletea components and other interactive views using +// user provided theme.json or defaults if not present package theme import ( @@ -25,6 +27,7 @@ type ColorSpec struct { Dark string `json:"dark,omitempty"` } +// UnmarshalJSON handles both string and object forms for a ColorSpec. func (c *ColorSpec) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err == nil { @@ -35,6 +38,7 @@ func (c *ColorSpec) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, (*alt)(c)) } +// IsAdaptive reports whether the ColorSpec has light/dark variants. func (c ColorSpec) IsAdaptive() bool { return c.Light != "" || c.Dark != "" } @@ -53,6 +57,7 @@ type StyleDef struct { Reverse bool `json:"reverse,omitempty"` } +// ThemeConfig is the raw JSON representation of a user theme. type ThemeConfig struct { Palette map[string]ColorSpec `json:"palette"` Styles map[string]StyleDef `json:"styles"` @@ -63,6 +68,7 @@ type ThemeConfig struct { // Compiled Theme (immutable) // ----------------------------------------------------------------------------- +// Theme is a compiled, ready-to-use theme with resolved colors and styles. type Theme struct { hasDarkBg bool colors map[string]color.Color @@ -70,6 +76,7 @@ type Theme struct { huhDefinitions map[string]StyleDef } +// NewTheme compiles a ThemeConfig into a Theme with resolved colors and styles. func NewTheme(cfg *ThemeConfig) *Theme { if cfg == nil { cfg = DefaultThemeConfig() @@ -94,6 +101,7 @@ func NewTheme(cfg *ThemeConfig) *Theme { return t } +// Color looks up a named color from the palette, falling back to parsing the name as a literal color. func (t *Theme) Color(name string) color.Color { if c, ok := t.colors[name]; ok { return c @@ -101,6 +109,7 @@ func (t *Theme) Color(name string) color.Color { return parseLiteralColor(name) } +// Style looks up a named style, returning an empty style if the name is not found. func (t *Theme) Style(name string) lipgloss.Style { if s, ok := t.styles[name]; ok { return s @@ -108,6 +117,7 @@ func (t *Theme) Style(name string) lipgloss.Style { return lipgloss.NewStyle() } +// HuhTheme returns a huh.ThemeFunc that overlays custom styles onto a base theme. func (t *Theme) HuhTheme(interactive bool) huh.ThemeFunc { return huh.ThemeFunc(func(isDark bool) *huh.Styles { base := huh.ThemeBase(isDark) @@ -186,6 +196,7 @@ func (t *Theme) HuhTheme(interactive bool) huh.ThemeFunc { // File Loading // ----------------------------------------------------------------------------- +// LoadThemeConfig reads and parses a theme.json file into a ThemeConfig. func LoadThemeConfig(path string) (*ThemeConfig, error) { if path == "" { return nil, nil @@ -210,6 +221,7 @@ func LoadThemeConfig(path string) (*ThemeConfig, error) { // Internal // ----------------------------------------------------------------------------- +// DefaultThemeConfig returns the built-in default theme configuration. func DefaultThemeConfig() *ThemeConfig { return &ThemeConfig{ Palette: map[string]ColorSpec{ diff --git a/internal/web/web.go b/internal/web/web.go index c0b2dab..8700f04 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -1,3 +1,5 @@ +// Package web is used to grep web title if not provided and handles basic +// open method package web import ( @@ -13,6 +15,7 @@ import ( "github.com/PuerkitoBio/goquery" ) +// OpenURL opens the given URL in the default browser. func OpenURL(url string) error { switch runtime.GOOS { case "windows": @@ -24,6 +27,7 @@ func OpenURL(url string) error { } } +// WebsiteTitle fetches and extracts the page title from a URL. func WebsiteTitle(url string) (string, error) { res, err := http.Get(url) if err != nil { @@ -47,6 +51,7 @@ func WebsiteTitle(url string) (string, error) { } } +// LoadWebsite fetches a page title with a spinner and 10-second timeout. func LoadWebsite(url string) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() diff --git a/main.go b/main.go index f4b6eb2..101be1f 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,4 @@ +// Package main is the entry point for the book CLI. package main import ( From bf7c3bc75567977d635d238f8b9ddadd5b6be982 Mon Sep 17 00:00:00 2001 From: Aaron Martell Date: Mon, 13 Jul 2026 16:11:13 -0500 Subject: [PATCH 2/2] go fmt --- internal/book/templates.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/internal/book/templates.go b/internal/book/templates.go index b4a6ac4..bea7a12 100644 --- a/internal/book/templates.go +++ b/internal/book/templates.go @@ -8,32 +8,40 @@ type Templatable interface { } // Primary returns an empty string for BookShelves. -func (bs *BookShelves) Primary() string { return "" } +func (bs *BookShelves) Primary() string { return "" } + // Secondary returns an empty string for BookShelves. func (bs *BookShelves) Secondary() string { return "" } + // List returns the names of all shelves. -func (bs *BookShelves) List() []string { return bs.ShelfNames() } +func (bs *BookShelves) List() []string { return bs.ShelfNames() } // Primary returns the shelf name. -func (s *Shelf) Primary() string { return s.Name } +func (s *Shelf) Primary() string { return s.Name } + // Secondary returns an empty string for Shelf. func (s *Shelf) Secondary() string { return "" } + // List returns the names of all collections in the shelf. -func (s *Shelf) List() []string { return s.CollectionsNames() } +func (s *Shelf) List() []string { return s.CollectionsNames() } // Primary returns the parent shelf name. -func (c *Collection) Primary() string { return c.Shelf.Name } +func (c *Collection) Primary() string { return c.Shelf.Name } + // Secondary returns the collection name. func (c *Collection) Secondary() string { return c.Name } + // List returns the names of all marks in the collection. -func (c *Collection) List() []string { return c.MarksNames() } +func (c *Collection) List() []string { return c.MarksNames() } // Primary returns the mark title. -func (m *Mark) Primary() string { return m.Name } +func (m *Mark) Primary() string { return m.Name } + // Secondary returns the mark URL. func (m *Mark) Secondary() string { return m.URL } + // List returns the mark's tags. -func (m *Mark) List() []string { return m.Tags } +func (m *Mark) List() []string { return m.Tags } // ViewTemplate used to templatize TUI success screens type ViewTemplate struct {