🏡 index : quine-global/hyper.quineglobal.com.git

// 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)
}