109 lines
3.4 KiB
Go
109 lines
3.4 KiB
Go
// Package HTML holds all the common HTML components and utilities.
|
|
package html
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
. "maragu.dev/gomponents"
|
|
. "maragu.dev/gomponents/components"
|
|
. "maragu.dev/gomponents/html"
|
|
)
|
|
|
|
var hashOnce sync.Once
|
|
var appCSSPath string
|
|
|
|
// PageProps are properties for the [page] component.
|
|
type PageProps struct {
|
|
Title string
|
|
Description string
|
|
}
|
|
|
|
// page layout with nav, main content, and footer.
|
|
func page(props PageProps, children ...Node) Node {
|
|
hashOnce.Do(func() {
|
|
appCSSPath = getHashedPath("public/styles/app.css")
|
|
})
|
|
|
|
return HTML5(HTML5Props{
|
|
Title: props.Title,
|
|
Description: props.Description,
|
|
Language: "en",
|
|
Head: []Node{
|
|
Link(Rel("stylesheet"), Href(appCSSPath)),
|
|
Link(Rel("preconnect"), Href("https://fonts.googleapis.com")),
|
|
Link(Rel("preconnect"), Href("https://fonts.gstatic.com"), Attr("crossorigin", "")),
|
|
Link(Rel("stylesheet"), Href("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap")),
|
|
Meta(Name("viewport"), Content("width=device-width, initial-scale=1")),
|
|
},
|
|
Body: Group{
|
|
siteNav(),
|
|
Main(Class("mx-auto max-w-5xl px-6 py-12"),
|
|
Group(children),
|
|
),
|
|
siteFooter(),
|
|
},
|
|
})
|
|
}
|
|
|
|
func siteNav() Node {
|
|
return Header(Class("border-b border-zinc-800 bg-zinc-950"),
|
|
Div(Class("mx-auto flex max-w-5xl items-center justify-between px-6 py-4"),
|
|
A(Href("/"), Class("flex items-center gap-3 no-underline"),
|
|
Img(Src("/images/hyper-logo.png"), Alt("Quine Hyper"), Class("h-8 w-8 rounded")),
|
|
Span(Class("font-mono text-lg font-bold tracking-tight text-white"),
|
|
Text("Quine Hyper"),
|
|
),
|
|
),
|
|
Div(Class("flex items-center gap-4"),
|
|
A(Href("https://github.com/quine-global/hyper"),
|
|
Class("font-mono text-sm text-zinc-400 hover:text-white transition-colors"),
|
|
Text("GitHub"),
|
|
),
|
|
A(Href("/download"),
|
|
Class("rounded-md bg-primary-500 px-4 py-2 font-mono text-sm font-semibold text-black hover:bg-primary-400 transition-colors"),
|
|
Text("Download"),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
}
|
|
|
|
func siteFooter() Node {
|
|
return Footer(Class("mt-24 border-t border-zinc-800 py-8"),
|
|
Div(Class("mx-auto flex max-w-5xl flex-col items-center justify-between gap-4 px-6 sm:flex-row"),
|
|
Div(Class("flex items-center gap-3"),
|
|
Img(Src("/images/quineglobal-logo.png"), Alt("Quine Global"), Class("h-6 w-6 rounded-full")),
|
|
Span(Class("font-mono text-sm text-zinc-500"),
|
|
Text("© 2026 Quine Global. Built on "),
|
|
A(Href("https://hyper.is"), Class("text-zinc-400 hover:text-white underline"), Text("Hyper")),
|
|
Text(" by Vercel."),
|
|
),
|
|
),
|
|
Div(Class("flex gap-6 font-mono text-sm"),
|
|
A(Href("/"), Class("text-zinc-500 hover:text-white transition-colors"), Text("Home")),
|
|
A(Href("https://github.com/quine-global/hyper"), Class("text-zinc-500 hover:text-white transition-colors"), Text("GitHub")),
|
|
A(Href("/download"), Class("text-zinc-500 hover:text-white transition-colors"), Text("Download")),
|
|
),
|
|
),
|
|
)
|
|
}
|
|
|
|
func getHashedPath(path string) string {
|
|
externalPath := strings.TrimPrefix(path, "public")
|
|
ext := filepath.Ext(path)
|
|
if ext == "" {
|
|
panic("no extension found")
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Sprintf("%v.x%v", strings.TrimSuffix(externalPath, ext), ext)
|
|
}
|
|
|
|
return fmt.Sprintf("%v.%x%v", strings.TrimSuffix(externalPath, ext), sha256.Sum256(data), ext)
|
|
}
|