mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-20 01:40:21 -05:00
224 lines
6.6 KiB
Go
224 lines
6.6 KiB
Go
package templates
|
|
|
|
import (
|
|
_ "embed"
|
|
|
|
"github.com/chasefleming/elem-go"
|
|
"github.com/chasefleming/elem-go/attrs"
|
|
"github.com/chasefleming/elem-go/styles"
|
|
)
|
|
|
|
//go:embed style.css
|
|
var headscaleCSS string
|
|
|
|
//go:embed headscale.svg
|
|
var headscaleSVG string
|
|
|
|
// mdTypesetBody creates a body element with md-typeset styling
|
|
// that matches the official Headscale documentation design.
|
|
// Uses CSS classes with styles defined in headscaleCSS.
|
|
func mdTypesetBody(children ...elem.Node) *elem.Element {
|
|
return elem.Body(attrs.Props{
|
|
attrs.Style: styles.Props{
|
|
styles.MinHeight: "100vh",
|
|
styles.Display: "flex",
|
|
styles.FlexDirection: "column",
|
|
styles.AlignItems: "center",
|
|
styles.BackgroundColor: "#ffffff",
|
|
styles.Padding: "3rem 1.5rem",
|
|
}.ToInline(),
|
|
"translate": "no",
|
|
},
|
|
elem.Div(attrs.Props{
|
|
attrs.Class: "md-typeset",
|
|
attrs.Style: styles.Props{
|
|
styles.MaxWidth: "min(800px, 90vw)",
|
|
styles.Width: "100%",
|
|
}.ToInline(),
|
|
}, children...),
|
|
)
|
|
}
|
|
|
|
// Styled Element Wrappers
|
|
// These functions wrap elem-go elements using CSS classes.
|
|
// Styling is handled by the CSS in headscaleCSS.
|
|
|
|
// H1 creates a H1 element styled by .md-typeset h1
|
|
func H1(children ...elem.Node) *elem.Element {
|
|
return elem.H1(nil, children...)
|
|
}
|
|
|
|
// H2 creates a H2 element styled by .md-typeset h2
|
|
func H2(children ...elem.Node) *elem.Element {
|
|
return elem.H2(nil, children...)
|
|
}
|
|
|
|
// H3 creates a H3 element styled by .md-typeset h3
|
|
func H3(children ...elem.Node) *elem.Element {
|
|
return elem.H3(nil, children...)
|
|
}
|
|
|
|
// P creates a paragraph element styled by .md-typeset p
|
|
func P(children ...elem.Node) *elem.Element {
|
|
return elem.P(nil, children...)
|
|
}
|
|
|
|
// Ol creates an ordered list element styled by .md-typeset ol
|
|
func Ol(children ...elem.Node) *elem.Element {
|
|
return elem.Ol(nil, children...)
|
|
}
|
|
|
|
// Ul creates an unordered list element styled by .md-typeset ul
|
|
func Ul(children ...elem.Node) *elem.Element {
|
|
return elem.Ul(nil, children...)
|
|
}
|
|
|
|
// A creates a link element styled by .md-typeset a
|
|
func A(href string, children ...elem.Node) *elem.Element {
|
|
return elem.A(attrs.Props{attrs.Href: href}, children...)
|
|
}
|
|
|
|
// Code creates an inline code element styled by .md-typeset code
|
|
func Code(children ...elem.Node) *elem.Element {
|
|
return elem.Code(nil, children...)
|
|
}
|
|
|
|
// Pre creates a preformatted text block styled by .md-typeset pre
|
|
func Pre(children ...elem.Node) *elem.Element {
|
|
return elem.Pre(nil, children...)
|
|
}
|
|
|
|
// PreCode creates a code block inside Pre styled by .md-typeset pre > code
|
|
func PreCode(code string) *elem.Element {
|
|
return elem.Code(nil, elem.Text(code))
|
|
}
|
|
|
|
// Deprecated: use H1, H2, H3 instead
|
|
func headerOne(text string) *elem.Element {
|
|
return H1(elem.Text(text))
|
|
}
|
|
|
|
// Deprecated: use H1, H2, H3 instead
|
|
func headerTwo(text string) *elem.Element {
|
|
return H2(elem.Text(text))
|
|
}
|
|
|
|
// Deprecated: use H1, H2, H3 instead
|
|
func headerThree(text string) *elem.Element {
|
|
return H3(elem.Text(text))
|
|
}
|
|
|
|
// contentContainer wraps page content with proper width.
|
|
// Content inside is left-aligned by default.
|
|
func contentContainer(children ...elem.Node) *elem.Element {
|
|
containerStyle := styles.Props{
|
|
styles.MaxWidth: "720px",
|
|
styles.Width: "100%",
|
|
styles.Display: "flex",
|
|
styles.FlexDirection: "column",
|
|
styles.AlignItems: "flex-start", // Left-align all children
|
|
}
|
|
|
|
return elem.Div(attrs.Props{attrs.Style: containerStyle.ToInline()}, children...)
|
|
}
|
|
|
|
// headscaleLogo returns the Headscale SVG logo for consistent branding across all pages.
|
|
// The logo is styled by the .headscale-logo CSS class.
|
|
func headscaleLogo() elem.Node {
|
|
// Return the embedded SVG as-is
|
|
return elem.Raw(headscaleSVG)
|
|
}
|
|
|
|
// pageFooter creates a consistent footer for all pages.
|
|
func pageFooter() *elem.Element {
|
|
footerStyle := styles.Props{
|
|
styles.MarginTop: space3XL,
|
|
styles.TextAlign: "center",
|
|
styles.FontSize: fontSizeSmall,
|
|
styles.Color: colorTextSecondary,
|
|
styles.LineHeight: lineHeightBase,
|
|
}
|
|
|
|
linkStyle := styles.Props{
|
|
styles.Color: colorTextSecondary,
|
|
styles.TextDecoration: "underline",
|
|
}
|
|
|
|
return elem.Div(attrs.Props{attrs.Style: footerStyle.ToInline()},
|
|
elem.Text("Powered by "),
|
|
elem.A(attrs.Props{
|
|
attrs.Href: "https://github.com/juanfont/headscale",
|
|
attrs.Rel: "noreferrer noopener",
|
|
attrs.Target: "_blank",
|
|
attrs.Style: linkStyle.ToInline(),
|
|
}, elem.Text("Headscale")),
|
|
)
|
|
}
|
|
|
|
// listStyle provides consistent styling for ordered and unordered lists
|
|
// EXTRACTED FROM: .md-typeset ol, .md-typeset ul CSS rules
|
|
var listStyle = styles.Props{
|
|
styles.LineHeight: lineHeightBase, // 1.6 - From .md-typeset
|
|
styles.MarginTop: "1em", // From CSS: margin-top: 1em
|
|
styles.MarginBottom: "1em", // From CSS: margin-bottom: 1em
|
|
styles.PaddingLeft: "clamp(1.5rem, 5vw, 2.5rem)", // Responsive indentation
|
|
}
|
|
|
|
// HtmlStructure creates a complete HTML document structure with proper meta tags
|
|
// and semantic HTML5 structure. The head and body elements are passed as parameters
|
|
// to allow for customization of each page.
|
|
// Styling is provided via a CSS stylesheet (Material for MkDocs design system) with
|
|
// minimal inline styles for layout and positioning.
|
|
func HtmlStructure(head, body *elem.Element) *elem.Element {
|
|
return elem.Html(attrs.Props{attrs.Lang: "en"},
|
|
elem.Head(nil,
|
|
elem.Meta(attrs.Props{
|
|
attrs.Charset: "UTF-8",
|
|
}),
|
|
elem.Meta(attrs.Props{
|
|
attrs.HTTPequiv: "X-UA-Compatible",
|
|
attrs.Content: "IE=edge",
|
|
}),
|
|
elem.Meta(attrs.Props{
|
|
attrs.Name: "viewport",
|
|
attrs.Content: "width=device-width, initial-scale=1.0",
|
|
}),
|
|
elem.Link(attrs.Props{
|
|
attrs.Rel: "icon",
|
|
attrs.Href: "/favicon.ico",
|
|
}),
|
|
// Google Fonts for Roboto and Roboto Mono
|
|
elem.Link(attrs.Props{
|
|
attrs.Rel: "preconnect",
|
|
attrs.Href: "https://fonts.gstatic.com",
|
|
"crossorigin": "",
|
|
}),
|
|
elem.Link(attrs.Props{
|
|
attrs.Rel: "stylesheet",
|
|
attrs.Href: "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono:wght@400;700&display=swap",
|
|
}),
|
|
// Material for MkDocs CSS styles
|
|
elem.Style(attrs.Props{attrs.Type: "text/css"}, elem.Raw(headscaleCSS)),
|
|
head,
|
|
),
|
|
body,
|
|
)
|
|
}
|
|
|
|
// BlankPage creates a minimal blank HTML page with favicon.
|
|
// Used for endpoints that need to return a valid HTML page with no content.
|
|
func BlankPage() *elem.Element {
|
|
return elem.Html(attrs.Props{attrs.Lang: "en"},
|
|
elem.Head(nil,
|
|
elem.Meta(attrs.Props{
|
|
attrs.Charset: "UTF-8",
|
|
}),
|
|
elem.Link(attrs.Props{
|
|
attrs.Rel: "icon",
|
|
attrs.Href: "/favicon.ico",
|
|
}),
|
|
),
|
|
elem.Body(nil),
|
|
)
|
|
}
|