ui

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package ui owns the dashboard's templates, static assets and rendering.

It deliberately depends on nothing but the standard library. Handlers live in internal/httpx and pass in whatever data a page needs, so this package can be tested by rendering a page and reading the HTML rather than by standing up services — and so a template change cannot quietly acquire a dependency on a service type.

Everything is embedded, because the deployment unit is one binary. There is no filesystem path to get wrong, no volume to forget to mount, and no way for the running instance to disagree with the templates it was built from.

Index

Constants

View Source
const StylesheetPath = "css/app.css"

StylesheetPath is the asset a build is expected to have generated.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bar

type Bar struct {
	X, Y, W, H int
	Day        string
	Clicks     int64
	Visitors   int64
	Bots       int64
}

type Chart

type Chart struct {
	W, H  int
	PlotH int
	MaxY  int64
	Bars  []Bar
	Ticks []Tick
	First string // label of the first day, for the x axis
	Last  string
}

Chart is a bar chart laid out in Go rather than in the browser.

The dashboard's charts are server-rendered SVG. A charting library would be the only piece of custom JavaScript in the product, for four rectangles and an axis — and the CSP disallows inline styles, which most of them generate. Computing integer geometry here keeps the template a dumb loop.

func BarChart

func BarChart(points []DayCount, w, h int) Chart

BarChart lays out a day series in a w×h viewBox.

Exported for its geometry tests; templates reach it through the func map.

type DayCount

type DayCount struct {
	Day      string // 2006-01-02
	Clicks   int64
	Visitors int64
	Bots     int64
}

DayCount is one day of a click series as the chart helpers consume it.

A local type rather than analytics.DayPoint, because this package depends on nothing outside the standard library. Handlers convert; the conversion is three assignments and it keeps a template change from ever pulling a service package into the UI.

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer holds the parsed template set and the fingerprinted asset table.

Templates are parsed once at boot. A syntax error therefore fails startup rather than the first request that happens to reach that page, which is the difference between a deploy that refuses to come up and one that looks healthy until someone clicks the wrong tab.

func New

func New() (*Renderer, error)

New parses every template and fingerprints every static asset.

func (*Renderer) AssetURL

func (r *Renderer) AssetURL(name string) string

AssetURL returns the fingerprinted URL for an asset, or the plain path if the asset is absent — a missing stylesheet should 404 visibly rather than render as an empty href.

func (*Renderer) MissingAssets

func (r *Renderer) MissingAssets() []string

MissingAssets reports expected assets that no build produced.

Returned rather than fatal: a stylesheet-less dashboard is ugly but working, and refusing to start would turn a forgotten build step into an outage. The caller logs it loudly at boot.

func (*Renderer) Pages

func (r *Renderer) Pages() []string

Pages lists the parsed page names, for the test that asserts every page renders.

func (*Renderer) Render

func (r *Renderer) Render(w http.ResponseWriter, status int, page string, data any) error

Render writes a full page.

Rendered into a buffer first. Executing straight to the ResponseWriter would commit a 200 and a half-written page the moment a template referenced a missing field, leaving the browser with truncated HTML and the operator with no error to look at.

func (*Renderer) RenderPartial

func (r *Renderer) RenderPartial(w http.ResponseWriter, status int, page, block string, data any) error

RenderPartial writes one named block, for an HTMX swap.

The block is looked up in the page's own template set, so a partial can use anything that page defines. Same buffering rule as Render.

func (*Renderer) StaticHandler

func (r *Renderer) StaticHandler(prefix string) http.Handler

StaticHandler serves the embedded assets.

Served from memory with a strong ETag and a one-year max-age. The long lifetime is safe because every URL the templates emit carries a content fingerprint: a new build changes the URL, so nothing can be served stale. Requests without the fingerprint still work and still validate.

type Tick

type Tick struct {
	Y     int
	Label string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL