From a1dfc548198a1326e71f1dd70303a5d3441f7a39 Mon Sep 17 00:00:00 2001
From: Alan Pearce
Date: Fri, 21 Jun 2024 13:02:08 +0200
Subject: refactor: switch to templ for HTML templates
---
internal/components/data.go | 37 ++++++++++++
internal/components/detail.templ | 20 +++++++
internal/components/error.templ | 18 ++++++
internal/components/homepage.templ | 10 ++++
internal/components/markdown.templ | 35 ++++++++++++
internal/components/optionDetail.templ | 58 +++++++++++++++++++
internal/components/options.templ | 34 +++++++++++
internal/components/packageDetail.templ | 99 +++++++++++++++++++++++++++++++++
internal/components/packages.templ | 37 ++++++++++++
internal/components/page.templ | 91 ++++++++++++++++++++++++++++++
internal/components/results.templ | 44 +++++++++++++++
internal/components/search.templ | 34 +++++++++++
12 files changed, 517 insertions(+)
create mode 100644 internal/components/data.go
create mode 100644 internal/components/detail.templ
create mode 100644 internal/components/error.templ
create mode 100644 internal/components/homepage.templ
create mode 100644 internal/components/markdown.templ
create mode 100644 internal/components/optionDetail.templ
create mode 100644 internal/components/options.templ
create mode 100644 internal/components/packageDetail.templ
create mode 100644 internal/components/packages.templ
create mode 100644 internal/components/page.templ
create mode 100644 internal/components/results.templ
create mode 100644 internal/components/search.templ
(limited to 'internal/components')
diff --git a/internal/components/data.go b/internal/components/data.go
new file mode 100644
index 0000000..64caeaa
--- /dev/null
+++ b/internal/components/data.go
@@ -0,0 +1,37 @@
+package components
+
+import (
+ "searchix/frontend"
+ "searchix/internal/config"
+ search "searchix/internal/index"
+ "searchix/internal/nix"
+
+ "github.com/blevesearch/bleve/v2"
+)
+
+type TemplateData struct {
+ Sources []*config.Source
+ Source config.Source
+ Query string
+ Results bool
+ SourceResult *bleve.SearchResult
+ ExtraHeadHTML string
+ Code int
+ Message string
+ Assets *frontend.AssetCollection
+}
+
+type ResultData struct {
+ TemplateData
+ Query string
+ ResultsPerPage int
+ Results *search.Result
+ Prev string
+ Next string
+}
+
+type DocumentData struct {
+ TemplateData
+ Document *nix.Importable
+ Children *search.Result
+}
diff --git a/internal/components/detail.templ b/internal/components/detail.templ
new file mode 100644
index 0000000..6d6710c
--- /dev/null
+++ b/internal/components/detail.templ
@@ -0,0 +1,20 @@
+package components
+
+import (
+ "searchix/internal/nix"
+)
+
+templ Detail(thing nix.Importable) {
+ switch thing.(type) {
+ case nix.Option:
+ @OptionDetail(thing.(nix.Option))
+ case nix.Package:
+ @PackageDetail(thing.(nix.Package))
+ }
+}
+
+templ DetailPage(tdata TemplateData, thing nix.Importable) {
+ @Page(tdata) {
+ @Detail(thing)
+ }
+}
diff --git a/internal/components/error.templ b/internal/components/error.templ
new file mode 100644
index 0000000..8e45095
--- /dev/null
+++ b/internal/components/error.templ
@@ -0,0 +1,18 @@
+package components
+
+import (
+ "strconv"
+)
+
+templ Error(tdata TemplateData) {
+
+ { strconv.Itoa(tdata.Code) }
+ { tdata.Message }
+
+}
+
+templ ErrorPage(tdata TemplateData) {
+ @Page(tdata) {
+ @Error(tdata)
+ }
+}
diff --git a/internal/components/homepage.templ b/internal/components/homepage.templ
new file mode 100644
index 0000000..1cc2b9e
--- /dev/null
+++ b/internal/components/homepage.templ
@@ -0,0 +1,10 @@
+package components
+
+templ Homepage(tdata TemplateData) {
+ @Page(tdata) {
+
+ Search Nix Packages and options from NixOS, Darwin and Home-Manager
+
+ Source code
+ }
+}
diff --git a/internal/components/markdown.templ b/internal/components/markdown.templ
new file mode 100644
index 0000000..2a8787d
--- /dev/null
+++ b/internal/components/markdown.templ
@@ -0,0 +1,35 @@
+package components
+
+import (
+ "regexp"
+
+ "searchix/internal/nix"
+
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/extension"
+ "context"
+ "io"
+)
+
+var (
+ md = goldmark.New(
+ goldmark.WithExtensions(extension.NewLinkify()),
+ )
+ firstSentenceRegexp = regexp.MustCompile(`^.*?\.[[:space:]]`)
+)
+
+func firstSentence[T ~string](text T) T {
+ if fs := firstSentenceRegexp.FindString(string(text)); fs != "" {
+ return T(fs)
+ }
+
+ return text
+}
+
+func markdown(text nix.Markdown) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
+ err := md.Convert([]byte(text), w)
+
+ return err
+ })
+}
diff --git a/internal/components/optionDetail.templ b/internal/components/optionDetail.templ
new file mode 100644
index 0000000..52ce859
--- /dev/null
+++ b/internal/components/optionDetail.templ
@@ -0,0 +1,58 @@
+package components
+
+import "searchix/internal/nix"
+
+templ OptionDetail(option nix.Option) {
+ { option.Name }
+ @markdown(option.Description)
+
+ if option.Type != "" {
+ - Type
+ { option.Type }
+ }
+ if option.Default != nil {
+ if option.Default.Text != "" || option.Default.Markdown != "" {
+ - Default
+ -
+ if option.Default.Markdown != "" {
+ @markdown(option.Default.Markdown)
+ } else {
+
{ option.Default.Text }
+ }
+
+ }
+ }
+ if option.Example != nil {
+ if option.Example.Text != "" || option.Example.Markdown != "" {
+ - Example
+ -
+ if option.Example.Markdown != "" {
+ @markdown(option.Example.Markdown)
+ } else {
+
{ option.Example.Text }
+ }
+
+ }
+ }
+ if option.RelatedPackages != "" {
+ - Related Packages
+ -
+ @markdown(option.RelatedPackages)
+
+ }
+ if len(option.Declarations) > 0 {
+ - Declared
+ for _, d := range option.Declarations {
+ -
+ { d.Name }
+
+ }
+ }
+
+}
+
+templ OptionDetailPage(tdata TemplateData, option nix.Option) {
+ @Page(tdata) {
+ @OptionDetail(option)
+ }
+}
diff --git a/internal/components/options.templ b/internal/components/options.templ
new file mode 100644
index 0000000..726d328
--- /dev/null
+++ b/internal/components/options.templ
@@ -0,0 +1,34 @@
+package components
+
+import (
+ "searchix/internal/index"
+ "searchix/internal/nix"
+)
+
+templ Options(result *index.Result) {
+
+
+
+ Title |
+ Description |
+
+
+
+ for _, hit := range result.Hits {
+ @optionRow(hit.Data.(nix.Option))
+ }
+
+
+}
+
+templ optionRow(o nix.Option) {
+
+
+ @openDialogLink(o.Name)
+ |
+
+ @markdown(firstSentence(o.Description))
+
+ |
+
+}
diff --git a/internal/components/packageDetail.templ b/internal/components/packageDetail.templ
new file mode 100644
index 0000000..7b4a5cb
--- /dev/null
+++ b/internal/components/packageDetail.templ
@@ -0,0 +1,99 @@
+package components
+
+import (
+ "searchix/internal/nix"
+)
+
+func licenseName(l nix.License) string {
+ if l.FullName != "" {
+ return l.FullName
+ } else {
+ return l.Name
+ }
+}
+
+templ PackageDetail(pkg nix.Package) {
+
+ if pkg.Broken {
+ { pkg.Attribute }
+ } else {
+ { pkg.Attribute }
+ }
+
+ if pkg.LongDescription != "" {
+ @markdown(pkg.LongDescription)
+ } else {
+ { pkg.Description }
+ }
+
+ if pkg.MainProgram != "" {
+ - Main Program
+ -
+
{ pkg.MainProgram }
+
+ }
+ if len(pkg.Homepages) > 0 {
+ - Homepage
+ -
+
+ for _, u := range pkg.Homepages {
+ -
+ { u }
+
+ }
+
+
+ }
+ if pkg.Version != "" {
+ - Version
+ - { pkg.Version }
+ }
+ if len(pkg.Licenses) > 0 {
+ - License
+ -
+
+ for _, l := range pkg.Licenses {
+ -
+ if l.URL != "" {
+ { licenseName(l) }
+ } else {
+ { licenseName(l) }
+ }
+ if l.AppendixURL != "" {
+ Appendix
+ }
+
+ }
+
+
+ }
+ if len(pkg.Maintainers) > 0 {
+ - Maintainers
+ -
+
+ for _, m := range pkg.Maintainers {
+ -
+ if m.Github != "" {
+ { m.Name }
+ } else {
+ { m.Name }
+ }
+
+ }
+
+
+ }
+ if pkg.Definition != "" {
+ - Defined
+ -
+ Source
+
+ }
+
+}
+
+templ PackageDetailPage(tdata TemplateData, pkg nix.Package) {
+ @Page(tdata) {
+ @PackageDetail(pkg)
+ }
+}
diff --git a/internal/components/packages.templ b/internal/components/packages.templ
new file mode 100644
index 0000000..4e00a5a
--- /dev/null
+++ b/internal/components/packages.templ
@@ -0,0 +1,37 @@
+package components
+
+import (
+ "searchix/internal/index"
+ "searchix/internal/nix"
+)
+
+templ Packages(result *index.Result) {
+
+
+
+ Attribute |
+ Name |
+ Description |
+
+
+
+ for _, hit := range result.Hits {
+ @packageRow(hit.Data.(nix.Package))
+ }
+
+
+}
+
+templ packageRow(p nix.Package) {
+
+
+ @openDialogLink(p.Attribute)
+ |
+
+ { p.Name }
+ |
+
+ { p.Description }
+ |
+
+}
diff --git a/internal/components/page.templ b/internal/components/page.templ
new file mode 100644
index 0000000..9b278e2
--- /dev/null
+++ b/internal/components/page.templ
@@ -0,0 +1,91 @@
+package components
+
+import (
+ "net/url"
+
+ "searchix/internal/config"
+ "searchix/frontend"
+)
+
+templ Page(tdata TemplateData) {
+
+
+
+
+
+ Searchix
+ for _, sheet := range tdata.Assets.Stylesheets {
+
+ }
+ @Unsafe(tdata.ExtraHeadHTML)
+ for _, source := range tdata.Sources {
+
+ }
+
+
+
+
+ { children... }
+
+
+
+
+}
+
+templ script(s *frontend.Asset) {
+
+}
+
+func Unsafe(html string) templ.Component {
+ return templ.ComponentFunc(func(_ context.Context, w io.Writer) (err error) {
+ _, err = io.WriteString(w, html)
+ return
+ })
+}
+
+func sourceNameAndType(source config.Source) string {
+ switch source.Importer {
+ case config.Options:
+ return source.Name + " " + source.Importer.String()
+ case config.Packages:
+ return source.Name
+ }
+ return ""
+}
+
+func joinPath(base string, parts ...string) templ.SafeURL {
+ u, err := url.JoinPath(base, parts...)
+ if err != nil {
+ panic(err)
+ }
+ return templ.SafeURL(u)
+}
+
+func joinPathQuery[T ~string](path T, query string) templ.SafeURL {
+ if query == "" {
+ return templ.SafeURL(path)
+ }
+ return templ.SafeURL(string(path) + "?query=" + url.QueryEscape(query))
+}
diff --git a/internal/components/results.templ b/internal/components/results.templ
new file mode 100644
index 0000000..3953cc3
--- /dev/null
+++ b/internal/components/results.templ
@@ -0,0 +1,44 @@
+package components
+
+import (
+ "strconv"
+ "searchix/internal/nix"
+)
+
+templ Results(r ResultData) {
+ if r.Query != "" {
+ if r.Results != nil && r.Results.Total > 0 {
+ switch r.Results.Hits[0].Data.(type) {
+ case nix.Option:
+ @Options(r.Results)
+ case nix.Package:
+ @Packages(r.Results)
+ }
+
+ } else {
+ Nothing found
+ }
+ } else {
+
+ }
+}
+
+templ ResultsPage(r ResultData) {
+ @SearchPage(r.TemplateData, r) {
+ @Results(r)
+ }
+}
+
+templ openDialogLink(attr string) {
+ { attr }
+}
diff --git a/internal/components/search.templ b/internal/components/search.templ
new file mode 100644
index 0000000..2cae754
--- /dev/null
+++ b/internal/components/search.templ
@@ -0,0 +1,34 @@
+package components
+
+templ Search(tdata TemplateData, r ResultData) {
+
+}
+
+templ SearchPage(tdata TemplateData, r ResultData) {
+ @Page(tdata) {
+ @script(tdata.Assets.ByPath["/static/search.js"])
+ @Search(tdata, r)
+
+
+ }
+}
--
cgit 1.4.1