210 lines · 9.3 KB
// Package report writes the two things a shoot produces: a page a person
// reads, and a file Claude reads.
//
// They are separate on purpose. A person wants the pictures side by side,
// worst first, in one file they can open from anywhere. A model wants a
// list of paths and numbers and no HTML at all -- handing it the report
// would spend most of a context window on base64.
package report

import (
	"bytes"
	"encoding/json"
	"html/template"
	"os"
	"path/filepath"
	"sort"
)

// Shot is one screen's outcome.
type Shot struct {
	Name string `json:"name"`
	// Before, After and Diff are paths, relative to the output directory.
	// Before and Diff are empty in review mode, which has no baseline.
	Before string `json:"before,omitempty"`
	After  string `json:"after"`
	Diff   string `json:"diff,omitempty"`
	// Moved says whether this shot changed enough to be worth a look.
	Moved bool `json:"moved"`
	// Verdict is the one-line summary: a pixel count, or why not.
	Verdict string `json:"verdict"`
	// Error is set when the shot could not be taken at all.
	Error string `json:"error,omitempty"`
	// Past is how many CSS pixels the rendering extends beyond the
	// viewport it was taken for, and Ink says whether anything is drawn
	// out there. A page wider than its phone scrolls sideways, which is
	// always a bug; whether there is content past the edge is what says
	// how big a one. Both are zero and false on a page that fits.
	Past int  `json:"overflow,omitempty"`
	Ink  bool `json:"overflow_ink,omitempty"`
	// Width and Height are the candidate rendering's size. The report