design

package
v0.700.0 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 46 Imported by: 0

Documentation

Overview

Package design implements Pando's Design Studio: HTML/CSS/JS design artifacts that agents build, render, inspect and iterate on.

An artifact is a directory in the user's working tree (designer/<slug>/ by default) holding an entry document, its assets and a portable pando-design.json manifest. Agents mutate those files with the regular write/edit tools, so permissions, diffs and agent-vcs keep working unchanged.

History is not a copy-per-version store: each accepted iteration takes a directory-scoped snapshot (internal/snapshot), so checking out an old version can never revert work outside the artifact directory. SQLite holds only the metadata needed to list and navigate artifacts.

Index

Constants

View Source
const (
	RuleImageAlt           = "a11y.image-alt"
	RuleControlName        = "a11y.control-name"
	RuleHeadingOrder       = "a11y.heading-order"
	RuleMissingH1          = "a11y.missing-h1"
	RuleContrast           = "a11y.contrast"
	RuleTapTarget          = "a11y.tap-target"
	RuleDocumentTitle      = "a11y.document-title"
	RuleConsoleError       = "runtime.console-error"
	RuleNetworkFailure     = "runtime.network-failure"
	RuleHorizontalOverflow = "layout.horizontal-overflow"
	RuleEmptyDocument      = "layout.empty-document"
	RuleDeckNoSlides       = "deck.no-slides"
	RuleDeckPageBreak      = "deck.page-break"
	RuleSystemUnlinked     = "system.unlinked"
	RuleSystemHardcoded    = "system.hardcoded"
)

Audit rule codes. They are stable identifiers: an issue travels to the UI, to the agent and into the critique history, and all three need to be able to group and suppress findings by something other than their prose.

View Source
const (
	// PolicyNone scores and reports but never blocks. It is for artifacts
	// where the brief is exploratory and an iteration budget is waste.
	PolicyNone = "none"
	// PolicyStandard gates on the score alone.
	PolicyStandard = "standard"
	// PolicyStrict also refuses to pass while any error-level finding remains,
	// and holds the score to a higher bar.
	PolicyStrict = "strict"
)

Critique policies. A policy decides how hard the gate is, not what the audit looks for: every rule runs under every policy, so the issue list a user reads is the same one either way.

View Source
const (
	// EventCreated fires once, when an artifact directory is materialised.
	EventCreated = "design.created"
	// EventVersion fires when an iteration is committed, which is what a
	// version timeline and a thumbnail strip listen for.
	EventVersion = "design.version"
	// EventRender fires after a successful render, carrying the fresh node
	// count so an open preview knows to reload.
	EventRender = "design.render"
	// EventCritique fires when a critic pass scores a version (P8).
	EventCritique = "design.critique"
)

Event kinds published by the design subsystem. They are the names the SSE stream carries, so surfaces can switch on them directly.

View Source
const (
	ExportHTML = "html"
	ExportPNG  = "png"
	ExportPDF  = "pdf"
)

Export formats supported in v1. Anything richer (Figma, video, sprite sheets) is deliberately out of scope.

View Source
const (
	SeverityInfo     = "info"
	SeverityWarning  = "warning"
	SeverityError    = "error"
	SeverityBlocking = "blocking"
)

Issue severities produced by the critic and the accessibility pass.

View Source
const (
	OpSetText     = "set_text"      // replace the element's children with escaped text
	OpSetHTML     = "set_html"      // replace the element's children with raw markup
	OpSetAttr     = "set_attr"      // add or update one attribute
	OpRemoveAttr  = "remove_attr"   // drop one attribute
	OpSetStyle    = "set_style"     // merge declarations into the inline style attribute
	OpAddClass    = "add_class"     // append a class if missing
	OpRemoveClass = "remove_class"  // drop a class if present
	OpInsertHTML  = "insert_html"   // insert markup relative to the element
	OpReplaceHTML = "replace_outer" // replace the whole element
	OpRemove      = "remove"        // delete the whole element
)

Patch operations. Each one targets a single element resolved by selector (or, through the node index, by the data-pando-id a render stamped on it).

View Source
const (
	PositionBefore  = "before"
	PositionAfter   = "after"
	PositionPrepend = "prepend"
	PositionAppend  = "append"
)

Insert positions accepted by OpInsertHTML.

View Source
const (
	// SystemTokensFile is the token source of truth.
	SystemTokensFile = "tokens.json"
	// SystemStylesheet is generated from the tokens.
	SystemStylesheet = "system.css"
)

The shared design system lives in designer/_system/ and is a plain pair of files: tokens.json (the source of truth, committed and diffable) and the system.css it generates. Artifacts opt in by linking system.css, so a token change is a one-file edit that every artifact picks up on its next render.

View Source
const DefaultSlideSelector = "[data-slide], .slide, section.slide"

DefaultSlideSelector matches the slide containers a deck is expected to use.

View Source
const ManifestName = "pando-design.json"

ManifestName is the portable per-artifact manifest, committed with the files.

View Source
const (
	// SystemContractFile is the prose half of the design system.
	SystemContractFile = "DESIGN.md"
)

The design system is a contract, not a prompt. tokens.json is the machine half — the values the stylesheet and the constraint block are generated from. DESIGN.md is the human half: the rules a reviewer reads and the designer is held to. Both are committed, and the generated part of DESIGN.md is fenced by markers so regenerating it never destroys what a person wrote around it.

Variables

View Source
var DefaultStyleProps = []string{
	"display", "position", "color", "background-color",
	"font-family", "font-size", "font-weight", "line-height",
	"margin", "padding", "border-radius", "text-align",
}

DefaultStyleProps is the computed-style subset carried in the node index. It is deliberately short: the index is fed to a model, so every extra property costs tokens on every node.

View Source
var DefaultViewport = Viewport{W: 1440, H: 900}

DefaultViewport is the render size used when a manifest does not set one.

View Source
var ErrBundleInstalled = errors.New("design: template already installed")

ErrBundleInstalled reports that a skill of that name is already present.

View Source
var ErrNoBrowser = fmt.Errorf("design: no browser available")

ErrNoBrowser is returned when no Chromium-family browser can be resolved. Callers degrade gracefully: the live preview is the user's own browser, so a missing Chromium only costs screenshots, inspection, export and rasterizing.

View Source
var ErrNoIndex = errors.New("design: no structure index")

ErrNoIndex reports that an artifact exists but carries no structure index for the requested version, which only a render can produce.

View Source
var ErrNoProvider = errors.New("design: the design subsystem is not available in this process")

ErrNoProvider is returned by the tools when the design subsystem was never wired, which happens in stripped-down entry points that run without a database.

View Source
var ErrNotFound = errors.New("design: not found")

ErrNotFound is returned when an artifact, version or node does not exist.

View Source
var ErrSchemaOutdated = errors.New("design: database schema is outdated; the design tables are missing; restart Pando so pending migrations run")

ErrSchemaOutdated reports a database created before the design migrations ran.

Functions

func BlendScore

func BlendScore(audited, written float64) float64

BlendScore combines the deterministic audit score with the critic's own. The deterministic pass cannot see whether a layout reads well and the critic cannot count contrast failures reliably, so neither is allowed to be the whole verdict. A critic that offers no score leaves the audit score standing.

func BundledTemplateContent

func BundledTemplateContent(name string) (string, bool)

BundledTemplateContent returns the raw SKILL.md of a bundled template, which is what the installer writes and what the gallery shows as "read the skill".

func CloseDefaultProvider

func CloseDefaultProvider()

CloseDefaultProvider shuts the shared browser down at application exit.

func ClosePreviewServer

func ClosePreviewServer()

ClosePreviewServer stops and forgets the process-wide preview server.

func ContrastRatio

func ContrastRatio(foreground, background RGB) float64

ContrastRatio returns the WCAG contrast ratio between a foreground and the surface behind it. A translucent foreground is composited over the background first: that is what the eye sees, and what the browser paints.

func CountsBySeverity

func CountsBySeverity(issues []Issue) map[string]int

CountsBySeverity summarises an issue list for a header line.

func CraftReference

func CraftReference(name string) (string, bool)

CraftReference returns one craft reference by name ("typography").

func CraftReferenceNames

func CraftReferenceNames() []string

CraftReferenceNames lists the bundled craft references.

func EnsurePreviewServer

func EnsurePreviewServer() (*preview.Server, error)

EnsurePreviewServer returns the installed preview server, starting a loopback one if there is none. Surfaces that need a URL call it; nothing starts a listener merely because the design package was imported.

func Events

func Events() *pubsub.Broker[Event]

Events returns the process-wide design event broker. It is created on first use so a process that never designs anything never allocates it.

func ExampleSystem

func ExampleSystem(name string) (string, bool)

ExampleSystem returns the prose of a bundled style guide.

func ExampleSystemNames

func ExampleSystemNames() []string

ExampleSystemNames lists the bundled style guides, sorted for a stable listing in every surface that offers them.

func ExampleSystemTitle

func ExampleSystemTitle(name string) string

ExampleSystemTitle returns the first heading of a bundled guide, which is what a picker should show next to its name.

func InstallBundle

func InstallBundle(name, targetDir string, force bool) ([]string, error)

InstallBundle writes a bundled template into a skills root as {targetDir}/{name}/SKILL.md plus the craft references it declares. It returns the paths written. Installing is what makes the bundle visible to the agent's skill loader; the gallery works without it.

It refuses to overwrite an existing skill unless force is set: the installed copy is the user's to edit, and silently replacing their edits with ours is the one thing an install must never do.

func MirrorPath

func MirrorPath(name string) string

MirrorPath is where a design system is mirrored in the knowledge base.

func NewArtifactID

func NewArtifactID() string

NewArtifactID returns a fresh artifact identifier.

func NewCritiqueID

func NewCritiqueID() string

NewCritiqueID returns a fresh critique identifier.

func NormalizePolicy

func NormalizePolicy(policy string) string

NormalizePolicy maps a written policy onto a known one, returning "" for anything unrecognised so the caller can keep its own default rather than silently running a policy nobody asked for.

func ParseSelectionURI

func ParseSelectionURI(uri string) (string, bool)

ParseSelectionURI extracts the node id from a selection reference.

func PreviewOptions

func PreviewOptions(baseURL func() string, access func() error) preview.Options

PreviewOptions builds the options every preview server in this process shares. baseURL and access may be nil for the loopback fallback, which serves its own origin and is unreachable from the network by construction.

func PreviewServer

func PreviewServer() *preview.Server

PreviewServer returns the installed preview server, or nil.

func PromptConstraints

func PromptConstraints() string

PromptConstraints returns the constraint block for the current project, or an empty string when the project has not committed a system. That still matters: a default system nobody chose is not a constraint worth stating.

func RuleCodes

func RuleCodes(issues []Issue) []string

RuleCodes lists the rules that fired, worst-severity first, for a compact one-line summary.

func Scaffold

func Scaffold(name, title string) (map[string]string, error)

Scaffold returns the seed files of a bundled template with the artifact title substituted, keyed by their path inside the artifact directory. A template with no scaffold returns an empty map and the caller falls back to the placeholder entry, which is still renderable.

func SelectionURI

func SelectionURI(nodeID string) string

SelectionURI formats the selection protocol every surface speaks.

func SetDefaultProvider

func SetDefaultProvider(p *Provider)

SetDefaultProvider installs the process-wide provider.

func SetPreviewServer

func SetPreviewServer(s *preview.Server)

SetPreviewServer installs the process-wide preview server. The API server calls it with an instance mounted on its own listener, so previews live on the Pando origin and inherit its bind address and its authentication.

func Slugify

func Slugify(title string) string

Slugify reduces a free-form title to a lowercase, hyphen-separated, filesystem-safe name. It never returns a path separator, a leading dot or a reserved name.

func SortedTokenGroups

func SortedTokenGroups(tokens map[string]map[string]string) []string

SortedTokenGroups and SortedTokenNames are the exported forms of the ordering every surface needs: a token table shown in a different order each time is unreadable, and the sort belongs here rather than in each renderer.

func SortedTokenNames

func SortedTokenNames(values map[string]string) []string

SortedTokenNames returns the token names of a group in stable order.

func ValidKind

func ValidKind(k Kind) bool

ValidKind reports whether k is a kind this version supports.

func WriteManifest

func WriteManifest(absDir string, m Manifest) error

WriteManifest writes pando-design.json into an artifact directory. The file is committed with the artifact, so it is formatted for humans to read.

Types

type ApplyResult

type ApplyResult struct {
	ArtifactID string `json:"artifact_id"`
	System     string `json:"system"`
	// Stylesheet is the artifact-relative href of the linked stylesheet.
	Stylesheet string `json:"stylesheet"`
	// Linked is true when this call added the link, false when it was already
	// there.
	Linked bool `json:"linked"`
	// Entry is the artifact-relative entry document.
	Entry string `json:"entry"`
	// Findings are hardcoded values a token already covers.
	Findings []SystemFinding `json:"findings,omitempty"`
	// Scanned counts the files audited.
	Scanned int `json:"scanned"`
	// Truncated is true when the finding list was cut short.
	Truncated bool `json:"truncated,omitempty"`
}

ApplyResult reports what applying the system did and what it found.

type Artifact

type Artifact struct {
	ID             string    `json:"id"` // dsg_<hex>
	SessionID      string    `json:"session_id,omitempty"`
	ProjectID      string    `json:"project_id,omitempty"`
	Title          string    `json:"title"`
	Slug           string    `json:"slug"`
	Dir            string    `json:"dir"` // project-relative, slash-separated
	Kind           Kind      `json:"kind"`
	SkillID        string    `json:"skill_id,omitempty"`
	DesignSystemID string    `json:"design_system_id,omitempty"`
	CurrentVersion int       `json:"current_version"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

Artifact is the metadata record of a design artifact. The files themselves live in Dir, relative to the project working directory.

type AuditInput

type AuditInput struct {
	// Rendered reports that the browser half of the pass actually happened.
	// Every accessibility, runtime, layout and deck rule reads a render, so
	// with no render they must not run at all: firing "the document has no
	// title" because nobody loaded the document is a finding about the audit,
	// not about the artifact.
	Rendered bool
	Kind     Kind
	Viewport Viewport
	Title    string
	Slides   int
	Width    float64
	Nodes    []Node
	Facts    []NodeFacts
	Console  []ConsoleEntry
	Failures []NetworkFailure
	// Breaks is the per-slide print behaviour, filled only when the caller
	// rendered under print emulation. Empty means the deck print rule is not
	// evaluated at all, rather than evaluated and passed.
	Breaks []SlideBreak
	// SystemLinked reports whether the entry document links the design system
	// stylesheet, and SystemFindings are the hardcoded values the P6 audit
	// found. RequiresSystem turns the unlinked finding on: an artifact built
	// from a template that declares no design system is not in breach.
	SystemLinked   bool
	RequiresSystem bool
	SystemFindings []SystemFinding
}

AuditInput is everything the deterministic quality pass reads. It is a plain struct rather than a service call so the rules can be tested against a hand-built render — no browser, no database, no project on disk.

type AuditResult

type AuditResult struct {
	Score   float64 `json:"score"`
	Summary string  `json:"summary"`
	Issues  []Issue `json:"issues"`
	// Counts is how many times each rule fired, including the occurrences that
	// were folded away by maxIssuesPerRule.
	Counts map[string]int `json:"counts,omitempty"`
}

AuditResult is one deterministic quality pass.

func Audit

func Audit(in AuditInput) AuditResult

Audit runs every deterministic rule over one render and scores the result. It never calls a model: this is the evidence a critic pass argues from, and evidence that changes between two identical runs is not evidence.

type BrowserAutoOpener

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

BrowserAutoOpener deduplicates best-effort browser launches for design artifacts so one artifact opens at most once automatically per process or session surface.

func NewBrowserAutoOpener

func NewBrowserAutoOpener() *BrowserAutoOpener

func (*BrowserAutoOpener) Open

func (o *BrowserAutoOpener) Open(artifactID, url string) error

type BrowserOptions

type BrowserOptions struct {
	// Type is a browser type understood by internal/browser ("chrome",
	// "chromium", "msedge", "brave", "lightpanda", …). Empty means auto-detect.
	Type string
	// Executable overrides the resolved browser binary.
	Executable string
	// Headless runs the browser without a window. Default: true.
	Headless bool
	// IdleTimeout closes the browser after this long without a render.
	// Zero means the session stays open until Close.
	IdleTimeout time.Duration
}

BrowserOptions configures how the design renderer launches a browser.

func BrowserOptionsFromConfig

func BrowserOptionsFromConfig() BrowserOptions

BrowserOptionsFromConfig reads the browser settings the browser_* tools already use, so Pando drives one browser configuration, not two.

type ConsoleEntry

type ConsoleEntry struct {
	Level   string `json:"level"`
	Message string `json:"message"`
}

ConsoleEntry is a JavaScript console message captured during a render.

type CreateParams

type CreateParams struct {
	Title        string
	Kind         Kind
	Slug         string
	SkillID      string
	DesignSystem string
	ProjectID    string
	// Files seeds the artifact directory: project-relative-to-the-artifact
	// paths mapped to their content. A scaffold normally provides at least the
	// entry document; when it is empty a minimal placeholder entry is written
	// so the artifact is renderable from version 1.
	Files map[string]string
	// Entry overrides the manifest entry document (default "index.html").
	Entry string
}

CreateParams describes a new artifact. Only Title is mandatory.

type Critique

type Critique struct {
	ID         string    `json:"id"`
	ArtifactID string    `json:"artifact_id"`
	Version    int       `json:"version"`
	Score      float64   `json:"score"` // 0-10
	Summary    string    `json:"summary,omitempty"`
	Issues     []Issue   `json:"issues"`
	CreatedAt  time.Time `json:"created_at"`
}

Critique is one critic pass over a version.

type CritiqueOptions

type CritiqueOptions struct {
	// Version critiques a specific version; 0 means the current one. Only the
	// current version can be re-rendered, so an older version is audited from
	// what was stored for it.
	Version int
	// Render overrides the render the audit runs on.
	Render RenderOptions
	// SkipRender audits without a browser: the design-system checks still run,
	// the accessibility, runtime and layout rules cannot.
	SkipRender bool
	// Round is the 1-based iteration number. Zero means "use the version
	// number", which is what an ordinary designer/critic loop wants: one
	// committed version per round.
	Round int
	// Policy overrides the resolved critique policy for this pass.
	Policy string

	// Score, Summary and Issues carry a critic's own judgement. They are
	// optional: a pass with none of them recorded is a purely deterministic
	// audit, which is exactly what a CI check wants.
	Score   float64
	Summary string
	Issues  []Issue

	// Record stores the critique against the version. Pass false for a dry
	// look that leaves no history behind.
	Record bool
}

CritiqueOptions drives one critic pass. The zero value is a valid deterministic pass over the current version.

type CritiqueReport

type CritiqueReport struct {
	Artifact Artifact         `json:"artifact"`
	Version  int              `json:"version"`
	Rendered bool             `json:"rendered"`
	Audit    AuditResult      `json:"audit"`
	Critique Critique         `json:"critique"`
	Decision GateDecision     `json:"decision"`
	Settings CritiqueSettings `json:"settings"`
	// Recorded is true when the critique was written to the history.
	Recorded bool `json:"recorded"`
	// RenderError explains why the render half of the audit is missing, when
	// it is. A pass that silently drops two thirds of its rules would report a
	// high score for a page nobody looked at.
	RenderError string `json:"render_error,omitempty"`
}

CritiqueReport is one complete critic pass: the evidence, the stored critique, and the decision the gate reached from them.

type CritiqueSettings

type CritiqueSettings struct {
	Enabled   bool    `json:"enabled"`
	MaxRounds int     `json:"max_rounds"`
	Threshold float64 `json:"threshold"`
	Policy    string  `json:"policy"`
}

CritiqueSettings bounds one designer/critic loop.

func DefaultCritiqueSettings

func DefaultCritiqueSettings() CritiqueSettings

DefaultCritiqueSettings reads the configured bounds, falling back to the package defaults when configuration has not been loaded — the CLI and the tests both reach the gate without a config in some paths, and a gate that panics there is worse than one that uses its defaults.

func (CritiqueSettings) Gate

func (s CritiqueSettings) Gate(c Critique, round int) GateDecision

Gate decides whether the loop stops after this critique. round is 1-based: the critique of the first version is round 1.

func (CritiqueSettings) WithPolicy

func (s CritiqueSettings) WithPolicy(policy string) CritiqueSettings

WithPolicy applies a per-skill override (od.critique.policy). An unknown or empty policy leaves the settings untouched: a template is allowed to say nothing about critique.

type DeckSpec

type DeckSpec struct {
	Slides     int    `json:"slides,omitempty"`
	Navigation string `json:"navigation,omitempty"` // "horizontal" | "vertical"
}

DeckSpec carries deck-only metadata. Slides is informational: the renderer re-counts slides on every render, since the files are edited by hand.

type DesignSystem

type DesignSystem struct {
	Name string `json:"name"`
	// Tokens maps a group ("color", "space", "font", ...) to its named values.
	// Custom properties are emitted as --<group>-<name>.
	Tokens map[string]map[string]string `json:"tokens"`
	// Fonts lists stylesheet URLs to import ahead of the custom properties.
	Fonts []string `json:"fonts,omitempty"`
}

DesignSystem is the token set shared by the artifacts of a project.

func DefaultDesignSystem

func DefaultDesignSystem() DesignSystem

DefaultDesignSystem is the starting point written by design_system init: a neutral, accessible base rather than an opinionated theme.

func LoadSystemAt

func LoadSystemAt(layout Layout) (DesignSystem, bool, error)

LoadSystemAt reads a design system from a layout without a Service, which is what the prompt builder needs: it runs before any session exists.

func (DesignSystem) CSS

func (ds DesignSystem) CSS() string

CSS renders the tokens as custom properties. Groups and names are sorted so that the generated stylesheet is byte-stable across runs and produces a clean diff when a single token changes.

func (DesignSystem) ConstraintBlock

func (ds DesignSystem) ConstraintBlock(stylesheetPath, contractPath string) string

ConstraintBlock renders the system as the hard constraint injected into the designer's prompt. It is deliberately short: it is paid for on every request where design is enabled, so it carries the values and the rule, and leaves the reasoning to DESIGN.md, which the agent can read when it needs to.

func (DesignSystem) Contract

func (ds DesignSystem) Contract() string

Contract renders a complete DESIGN.md for a system that has none yet. The prose is a starting point on purpose: the rules that matter to a project are the ones its designers write down, and an empty file invites nobody to write them.

func (DesignSystem) TokenSection

func (ds DesignSystem) TokenSection() string

TokenSection renders the generated, fenced half of DESIGN.md.

type ErrAmbiguousRef

type ErrAmbiguousRef struct {
	Ref       string
	Artifacts []Artifact
}

ErrAmbiguousRef is returned when a human-typed reference matches more than one artifact. It carries the candidates so a surface can show them.

func (*ErrAmbiguousRef) Error

func (e *ErrAmbiguousRef) Error() string

type Event

type Event struct {
	Kind         string `json:"kind"`
	ArtifactID   string `json:"artifact_id"`
	SessionID    string `json:"session_id,omitempty"`
	Title        string `json:"title,omitempty"`
	Slug         string `json:"slug,omitempty"`
	ArtifactKind Kind   `json:"artifact_kind,omitempty"`
	Version      int    `json:"version,omitempty"`
	Summary      string `json:"summary,omitempty"`
	// URL is the preview address when one could be minted, empty otherwise.
	URL string `json:"url,omitempty"`
	// Nodes is the size of the index a render produced.
	Nodes int `json:"nodes,omitempty"`
	// Slides is the deck slide count.
	Slides int `json:"slides,omitempty"`
	// Score is the critic score, for EventCritique.
	Score float64   `json:"score,omitempty"`
	At    time.Time `json:"at"`
}

Event is one design lifecycle notification. It is deliberately flat and small: it travels over SSE to every connected surface, and a surface that wants more calls the REST API for it.

type ExportOptions

type ExportOptions struct {
	// Format is html, png or pdf.
	Format string
	// Dest is the output file, absolute or relative to the working directory.
	// Empty writes next to the artifact under exports/.
	Dest string
	// Slide exports a single deck slide (PNG only); -1 for the whole document.
	Slide int
	// FullPage captures beyond the viewport for PNG exports.
	FullPage bool
	// Viewport overrides the manifest viewport.
	Viewport Viewport
	// Landscape applies to PDF exports without their own @page size.
	Landscape bool
}

ExportOptions configures one export.

type ExportResult

type ExportResult struct {
	Format string `json:"format"`
	Path   string `json:"path"`
	Bytes  int    `json:"bytes"`
	Note   string `json:"note,omitempty"`
}

ExportResult reports where an export landed.

type ExtractOptions

type ExtractOptions struct {
	// Source selects the extractor. Empty defaults to SourceCode.
	Source ExtractSource
	// Target is a directory (code), a URL, an image path or a markdown path.
	// Empty means the project root for code, and is an error otherwise.
	Target string
	// Name overrides the name given to the extracted system.
	Name string
	// MaxFiles bounds a code scan. Zero uses defaultExtractMaxFiles.
	MaxFiles int
}

ExtractOptions configures one extraction.

type ExtractResult

type ExtractResult struct {
	System  DesignSystem  `json:"system"`
	Source  ExtractSource `json:"source"`
	Target  string        `json:"target"`
	Scanned []string      `json:"scanned,omitempty"`
	Notes   []string      `json:"notes,omitempty"`
}

ExtractResult is an extracted system plus an account of how it was obtained, so a caller can show what was looked at before committing anything to disk.

type ExtractSource

type ExtractSource string

ExtractSource names where a design system is read from.

const (
	// SourceCode scans stylesheets and component files in a directory.
	SourceCode ExtractSource = "code"
	// SourceURL renders a page and reads its computed styles.
	SourceURL ExtractSource = "url"
	// SourceImage quantises a bitmap into a palette.
	SourceImage ExtractSource = "image"
	// SourceText reads a written style guide, including the bundled examples.
	SourceText ExtractSource = "text"
)

type GateDecision

type GateDecision struct {
	// Pass is true when this version meets the bar.
	Pass bool `json:"pass"`
	// Iterate is true when the loop should produce another version. It is not
	// simply !Pass: a run that has spent its rounds stops without passing.
	Iterate bool `json:"iterate"`
	// Reason is one sentence for a human and for the agent transcript.
	Reason    string  `json:"reason"`
	Round     int     `json:"round"`
	MaxRounds int     `json:"max_rounds"`
	Score     float64 `json:"score"`
	Threshold float64 `json:"threshold"`
	Policy    string  `json:"policy"`
	// Blocking counts the error- and blocking-severity findings that remain,
	// which is what a strict policy refuses to pass with.
	Blocking int `json:"blocking"`
}

GateDecision is the answer to the only question the loop asks: iterate again, or stop. It carries the numbers behind the answer so a surface can show why without recomputing them.

type ImageDiff

type ImageDiff struct {
	Width  int `json:"width"`
	Height int `json:"height"`
	// Changed is how many pixels differ by more than the tolerance, and Total
	// how many were compared.
	Changed int `json:"changed"`
	Total   int `json:"total"`
	// Fraction is Changed/Total, which is the number a regression check reads.
	Fraction float64 `json:"fraction"`
	// MaxDelta is the largest single-channel difference seen, so a caller can
	// tell "everything moved a shade" from "one region changed completely".
	MaxDelta int `json:"max_delta"`
	// SizeMismatch reports that the two images do not even have the same
	// dimensions, which is a regression on its own and makes Fraction 1.
	SizeMismatch bool `json:"size_mismatch"`
}

ImageDiff is the result of comparing two renders of the same artifact.

func ComparePNG

func ComparePNG(before, after []byte, tolerance int) (ImageDiff, error)

ComparePNG measures how much two PNG renders differ. tolerance is the per-channel difference below which two pixels count as the same; pass 0 for the default.

This is a perceptual-diff floor, not a perceptual model: it answers "did this render change" for a regression check, which is the question the fixture suite asks.

type InspectOptions

type InspectOptions struct {
	// NodeID restricts the result to one node and its descendants.
	NodeID string
	// Selector matches nodes whose selector or role contains this string.
	Selector string
	// Text matches nodes whose text contains this string (case-insensitive).
	Text string
	// Slide restricts the result to one deck slide; -1 for every slide.
	Slide int
	// Depth limits how far below the root nodes the result descends.
	// Zero means unlimited.
	Depth int
	// Offset and Limit page the result.
	Offset int
	Limit  int
	// IncludeStyles carries the computed-style subset. Off by default: styles
	// are the largest part of a node and are rarely needed for every node.
	IncludeStyles bool
	// StyleProps narrows which style properties survive when IncludeStyles is
	// set. Empty keeps all indexed properties.
	StyleProps []string
	// MaxTextLen truncates node text. Zero keeps the indexed text.
	MaxTextLen int
}

InspectOptions narrows and pages the structure index.

type InspectResult

type InspectResult struct {
	ArtifactID string `json:"artifact_id"`
	Version    int    `json:"version"`
	// Total is how many nodes matched before paging.
	Total  int    `json:"total"`
	Offset int    `json:"offset"`
	Limit  int    `json:"limit"`
	Nodes  []Node `json:"nodes"`
	// NextOffset is the offset of the next page, or -1 when this is the last.
	NextOffset int `json:"next_offset"`
}

InspectResult is one page of the structure index.

func Inspect

func Inspect(nodes []Node, opts InspectOptions) InspectResult

Inspect filters, trims and pages a node index. It works on any node slice, so the same code serves a fresh render and the stored index of an old version.

func (InspectResult) Text

func (r InspectResult) Text() string

Text renders an inspect page as compact lines for a tool result. One node per line keeps it readable for a model without spending a JSON envelope per node.

type Issue

type Issue struct {
	// Code is the stable rule identifier for a finding the deterministic audit
	// produced ("a11y.contrast"), empty for a finding a critic wrote in prose.
	// It is what lets a UI group findings and a caller suppress a rule without
	// matching on its message.
	Code     string `json:"code,omitempty"`
	Severity string `json:"severity"`
	NodeID   string `json:"node_id,omitempty"`
	Slide    int    `json:"slide,omitempty"`
	Message  string `json:"message"`
	Fix      string `json:"fix,omitempty"`
}

Issue is a single actionable finding against a version. NodeID (and Slide for decks) let the UI turn an issue into a selection.

func MergeIssues

func MergeIssues(audited, written []Issue) []Issue

MergeIssues folds a critic's own findings into the deterministic ones, dropping a critic finding that only restates a rule already fired on the same node. The audit is the evidence; the critic adds judgement, not an echo.

type Kind

type Kind string

Kind enumerates the artifact kinds supported in v1. Further kinds (mobile, document, dashboard, diagram) are deliberately deferred.

const (
	// KindWeb is a web prototype: one or more pages rendered at a viewport.
	KindWeb Kind = "web"
	// KindDeck is a slide deck: a single document whose slides are addressed by
	// index and exported one per PDF page.
	KindDeck Kind = "deck"
)

type Layout

type Layout struct {
	// WorkingDir is the absolute project root.
	WorkingDir string
	// OutputDir is the project-relative root holding artifacts ("designer").
	OutputDir string
	// SystemDir is the design-system directory inside OutputDir ("_system").
	SystemDir string
}

Layout resolves artifact directories inside a project. Every path handed to the rest of the package goes through it, so nothing can address a directory outside the configured output root.

func NewLayout

func NewLayout(workingDir, outputDir, systemDir string) Layout

NewLayout builds a Layout, filling empty fields with the defaults.

func (Layout) AbsDir

func (l Layout) AbsDir(relDir string) (string, error)

AbsDir returns the absolute directory for a project-relative artifact dir, rejecting anything that escapes the output root.

func (Layout) AvailableSlug

func (l Layout) AvailableSlug(title string) (string, error)

AvailableSlug turns title into a directory-safe slug and appends a numeric suffix until it names a directory that does not exist yet.

func (Layout) EnsureRoot

func (l Layout) EnsureRoot() error

EnsureRoot creates the output root if it does not exist yet.

func (Layout) RelDir

func (l Layout) RelDir(slug string) string

RelDir returns the project-relative directory of an artifact slug.

func (Layout) Root

func (l Layout) Root() string

Root returns the absolute path of the artifact output root.

func (Layout) SystemPath

func (l Layout) SystemPath() string

SystemPath returns the absolute path of the design-system directory.

type Manifest

type Manifest struct {
	ID           string      `json:"id"`
	Kind         Kind        `json:"kind"`
	Title        string      `json:"title,omitempty"`
	Version      int         `json:"version"`
	Entry        string      `json:"entry"`
	DesignSystem string      `json:"designSystem,omitempty"`
	Skill        string      `json:"skill,omitempty"`
	Preview      PreviewSpec `json:"preview"`
	Deck         *DeckSpec   `json:"deck,omitempty"`
}

Manifest is pando-design.json: the portable, committable description of an artifact. It is the source of truth when the SQLite metadata is missing (a fresh clone, another machine), so a design directory alone is enough to re-adopt the artifact.

func NewManifest

func NewManifest(id string, kind Kind, title string) Manifest

NewManifest builds a manifest with the defaults for a kind.

func ReadManifest

func ReadManifest(absDir string) (Manifest, error)

ReadManifest loads pando-design.json from an artifact directory.

func (*Manifest) Normalize

func (m *Manifest) Normalize()

Normalize fills in missing fields so a hand-edited manifest stays usable.

type NetworkFailure

type NetworkFailure struct {
	URL    string `json:"url"`
	Status int    `json:"status,omitempty"`
	Error  string `json:"error,omitempty"`
}

NetworkFailure is a request that failed or answered with an error status while rendering. Broken assets are a design defect, so they are reported to the agent with the render result instead of being swallowed.

type Node

type Node struct {
	ArtifactID string            `json:"artifact_id"`
	Version    int               `json:"version"`
	NodeID     string            `json:"node_id"`
	ParentID   string            `json:"parent_id,omitempty"`
	Selector   string            `json:"selector,omitempty"`
	Role       string            `json:"role,omitempty"`
	Text       string            `json:"text,omitempty"`
	Slide      int               `json:"slide,omitempty"` // deck only
	Box        Rect              `json:"box"`
	Styles     map[string]string `json:"styles,omitempty"`
}

Node is one entry of the structure index the inspector builds after a render. NodeID is the stable data-pando-id attribute injected at render time and is what a UI selection resolves to (design://<node_id>).

type NodeFacts

type NodeFacts struct {
	NodeID string `json:"node_id"`
	Tag    string `json:"tag"`
	// Name is an approximation of the accessible name: enough to tell a named
	// control from an unnamed one.
	Name string `json:"name"`
	// AltPresent distinguishes an image with alt="" — decorative on purpose —
	// from one that simply has no alt attribute at all.
	AltPresent   bool    `json:"alt_present"`
	HeadingLevel int     `json:"heading_level"`
	Interactive  bool    `json:"interactive"`
	AriaHidden   bool    `json:"aria_hidden"`
	HasText      bool    `json:"has_text"`
	Color        string  `json:"color"`
	Background   string  `json:"background"`
	FontSize     float64 `json:"font_size"`
	FontWeight   int     `json:"font_weight"`
	// Slide and Box are copied from the matching node so a rule can report a
	// finding without a second lookup.
	Slide int  `json:"slide"`
	Box   Rect `json:"box"`
}

NodeFacts is what the audit needs to know about one rendered element beyond what the node index stores.

type PatchChange

type PatchChange struct {
	Op       string `json:"op"`
	Selector string `json:"selector"`
	Matches  int    `json:"matches"`
	Detail   string `json:"detail,omitempty"`
}

PatchChange reports what one operation did, for the tool response and the permission prompt.

func ApplyPatch

func ApplyPatch(src []byte, ops []PatchOp) ([]byte, []PatchChange, error)

ApplyPatch applies ops to the HTML source and returns the new bytes together with a description of every change. The source is spliced, never reserialised: bytes outside the targeted ranges are preserved exactly.

type PatchFilePlan

type PatchFilePlan struct {
	// RelPath is relative to the artifact directory; Path is absolute.
	RelPath string        `json:"rel_path"`
	Path    string        `json:"-"`
	Old     string        `json:"-"`
	New     string        `json:"-"`
	Changes []PatchChange `json:"changes"`
}

PatchFilePlan is the pending rewrite of one artifact file.

func (PatchFilePlan) Diff

func (p PatchFilePlan) Diff() (string, int, int)

Diff renders a unified diff of the pending rewrite.

type PatchOp

type PatchOp struct {
	NodeID   string            `json:"node_id,omitempty"`
	Selector string            `json:"selector,omitempty"`
	File     string            `json:"file,omitempty"`
	Op       string            `json:"op"`
	Attr     string            `json:"attr,omitempty"`
	Value    string            `json:"value,omitempty"`
	Style    map[string]string `json:"style,omitempty"`
	Class    string            `json:"class,omitempty"`
	HTML     string            `json:"html,omitempty"`
	Position string            `json:"position,omitempty"`
	// All allows an operation to apply to every match instead of failing when a
	// selector is ambiguous.
	All bool `json:"all,omitempty"`
}

PatchOp is one edit request. Exactly one of NodeID or Selector must be set; File defaults to the artifact's entry document.

type PatchPlan

type PatchPlan struct {
	Artifact Artifact        `json:"artifact"`
	Files    []PatchFilePlan `json:"files"`
}

PatchPlan is the resolved, not-yet-written result of a design_patch call. It exists so the tool layer can show a real diff in the permission prompt before anything touches the user's working tree.

func (*PatchPlan) Empty

func (p *PatchPlan) Empty() bool

Empty reports whether the plan would change nothing.

type Presentation

type Presentation struct {
	ArtifactID string `json:"artifact_id"`
	Title      string `json:"title"`
	Kind       Kind   `json:"kind"`
	Version    int    `json:"version"`
	// Dir and Entry are project-relative.
	Dir   string `json:"dir"`
	Entry string `json:"entry"`
	// URL is the address to open. It is a served preview URL whenever a preview
	// server is running in this process, and a file:// address otherwise, so a
	// surface never has to ask which mode it is in.
	URL string `json:"url"`
	// FileURL is always the file:// address of the entry document. Exports and
	// "reveal in file manager" want the file, not the server.
	FileURL string `json:"file_url"`
	// BridgeURL is the preview URL with the selection bridge enabled. Only the
	// Pando UI loads it: it is what makes click-to-select work in the iframe.
	// Empty when no preview server is running.
	BridgeURL string `json:"bridge_url,omitempty"`
	// Slides is the deck slide count, zero for other kinds.
	Slides int `json:"slides,omitempty"`
	// Slide is the slide to open at, zero when unset.
	Slide int `json:"slide,omitempty"`
	// Selection is the design://<node_id> reference of the focused element.
	Selection string `json:"selection,omitempty"`
}

Presentation is what a surface needs to show an artifact to the user: where to open it, which slide, and which element is selected.

URL is a file:// address until the preview server exists; every surface reads it from here, so pointing them all at the served preview later is a change in one place.

func ResolveCreatedArtifactPresentation

func ResolveCreatedArtifactPresentation(ctx context.Context, artifactID string) (Presentation, error)

ResolveCreatedArtifactPresentation resolves the URL a newly created artifact should be opened with. It upgrades a file:// presentation to a served preview when this process can start a preview server.

type PreviewSpec

type PreviewSpec struct {
	Viewport Viewport `json:"viewport"`
}

PreviewSpec holds render defaults carried with the artifact.

type PrintOptions

type PrintOptions struct {
	RenderOptions
	// Landscape prints in landscape orientation.
	Landscape bool
	// PaperWidth and PaperHeight are in inches; zero uses the page's own CSS
	// size, which is what makes one-slide-per-page decks work.
	PaperWidth  float64
	PaperHeight float64
	// NoBackground omits background graphics. They print by default: a design
	// without its backgrounds is not the design.
	NoBackground bool
}

PrintOptions configures PDF export.

type Provider

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

Provider owns the process-wide pieces of the Design Studio — the database handle, the snapshot service and the single headless browser — and hands out cheap per-session Service values.

The renderer is shared on purpose: starting a browser costs far more than a tool call should, and every design surface (tools, HTTP preview, CLI) renders through the same one.

func DefaultProvider

func DefaultProvider() *Provider

DefaultProvider returns the process-wide provider, or nil when the design subsystem was never wired.

func NewProvider

func NewProvider(db *sql.DB) (*Provider, error)

NewProvider builds a provider over an open database. The snapshot service is created here because artifact versions are directory-scoped snapshots.

func NewProviderWith

func NewProviderWith(db *sql.DB, snaps Snapshotter) *Provider

NewProviderWith builds a provider over an explicit snapshotter, which is what tests use.

func (*Provider) Close

func (p *Provider) Close()

Close releases the shared browser.

func (*Provider) Service

func (p *Provider) Service(sessionID string) *Service

Service returns a design service bound to a session. The returned value is cheap: it shares the store, the snapshotter and the renderer.

func (*Provider) SetMirror

func (p *Provider) SetMirror(m SystemMirror)

SetMirror attaches the knowledge-base mirror handed to every service the provider creates. Wired at start-up when the knowledge base is available.

type RGB

type RGB struct {
	R, G, B float64
	A       float64
}

RGB is an 8-bit colour with an alpha channel, as a CSS colour value carries.

func ParseCSSColor

func ParseCSSColor(value string) (RGB, bool)

ParseCSSColor reads the colour notations a computed style actually produces — rgb(), rgba(), and the hex forms an inline style may still carry. It returns false for anything else (named colours, colour functions), because a rule that guesses at a colour reports contrast failures that are not real.

type Rect

type Rect struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
	W float64 `json:"w"`
	H float64 `json:"h"`
}

Rect is a layout box in CSS pixels, as reported by the renderer.

type RenderOptions

type RenderOptions struct {
	// URL overrides the document to load. Empty renders the artifact's entry
	// document from disk (file://); the preview server takes over in P3.
	URL string
	// Viewport overrides the artifact manifest viewport.
	Viewport Viewport
	// Wait is an extra settle delay after the load event, for pages that build
	// themselves in script.
	Wait time.Duration
	// MaxNodes and MaxDepth bound the structure index.
	MaxNodes int
	MaxDepth int
	// StyleProps overrides the computed-style subset.
	StyleProps []string
	// SlideSelector overrides the deck slide selector.
	SlideSelector string
	// PrintMedia renders under print emulation instead of screen.
	PrintMedia bool
}

RenderOptions configures one render.

type RenderResult

type RenderResult struct {
	URL       string           `json:"url"`
	Title     string           `json:"title"`
	Viewport  Viewport         `json:"viewport"`
	Slides    int              `json:"slides"`
	Nodes     []Node           `json:"nodes"`
	Truncated bool             `json:"truncated,omitempty"`
	Console   []ConsoleEntry   `json:"console,omitempty"`
	Failures  []NetworkFailure `json:"failures,omitempty"`
	Height    float64          `json:"height"`
	// Width is the document scroll width, which is how a horizontal overflow
	// is detected: a page wider than its own viewport.
	Width float64 `json:"width,omitempty"`
	// Facts carries the per-node accessibility and typography detail the
	// quality audit runs on. It is never persisted and never travels to a
	// model as part of the structure index: it exists for the lifetime of one
	// render, so the index itself stays as small as the inspector needs.
	Facts []NodeFacts `json:"-"`
}

RenderResult is what one render reports back to the agent.

type Renderer

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

Renderer drives a headless browser over design artifacts: render + index, screenshots, PDF printing and canvas rasterization.

func NewRenderer

func NewRenderer(layout Layout, opts BrowserOptions) *Renderer

NewRenderer builds a renderer. The browser is started lazily on first use, so constructing one is free on machines with no Chromium.

func (*Renderer) Available

func (r *Renderer) Available() bool

Available reports whether a usable browser can be resolved, without starting one. Callers use it to degrade instead of failing a whole design flow.

func (*Renderer) Close

func (r *Renderer) Close()

Close releases the browser.

func (*Renderer) EntryURL

func (r *Renderer) EntryURL(a Artifact) (string, error)

EntryURL returns the file:// URL of an artifact's entry document.

func (*Renderer) PrintPDF

func (r *Renderer) PrintPDF(ctx context.Context, a Artifact, opts PrintOptions) ([]byte, error)

PrintPDF renders the artifact under print emulation and prints it to PDF. Deck exports rely on the artifact's own @page rules, so preferCSSPageSize is always on.

func (*Renderer) Rasterize

func (r *Renderer) Rasterize(ctx context.Context, html string, width, height int, wait time.Duration) ([]byte, error)

Rasterize renders an HTML document that draws into a canvas (or any markup) and captures the result as a PNG. This is Pando's image-generation path: the browser is the renderer, so no image-model provider is involved.

func (*Renderer) Render

func (r *Renderer) Render(ctx context.Context, a Artifact, opts RenderOptions) (RenderResult, error)

Render loads an artifact, stamps data-pando-id on its elements and returns the structure index together with whatever the page logged or failed to load.

func (*Renderer) Screenshot

func (r *Renderer) Screenshot(ctx context.Context, a Artifact, opts ScreenshotOptions) ([]byte, error)

Screenshot renders the artifact and captures a PNG.

func (*Renderer) SlideBreaks

func (r *Renderer) SlideBreaks(ctx context.Context, a Artifact, opts RenderOptions) ([]SlideBreak, error)

SlideBreaks reports how each slide behaves under print emulation. A deck whose slides do not break after each other will not export one slide per page.

type ScreenshotOptions

type ScreenshotOptions struct {
	RenderOptions
	// Selector captures a single element instead of the page.
	Selector string
	// Slide captures one deck slide; -1 for the whole document.
	Slide int
	// FullPage captures beyond the viewport.
	FullPage bool
	// Quality is the PNG/JPEG quality passed to the browser (0 keeps the
	// browser default).
	Quality int
}

ScreenshotOptions selects what a screenshot captures.

type Service

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

Service is the artifact and version API the design tools, the HTTP surface and the CLI all share.

func NewService

func NewService(store *Store, snaps Snapshotter, layout Layout, sessionID string) *Service

NewService builds a design service. sessionID is recorded on artifacts and on the snapshots they take; it may be empty for CLI use.

func NewServiceFromConfig

func NewServiceFromConfig(db *sql.DB, snaps Snapshotter, sessionID string) *Service

NewServiceFromConfig builds a design service from the loaded configuration, which is how the tools, the HTTP surface and the CLI all obtain one.

func ServiceFor

func ServiceFor(sessionID string) (*Service, error)

ServiceFor returns a session-bound service from the default provider.

func (*Service) AbsDir

func (s *Service) AbsDir(a Artifact) (string, error)

AbsDir returns the absolute directory of an artifact.

func (*Service) ApplyPatchPlan

func (s *Service) ApplyPatchPlan(ctx context.Context, plan *PatchPlan, summary string, commit bool) (int, error)

ApplyPatchPlan writes a prepared plan to disk. When commit is true a new version (a directory-scoped snapshot) is recorded and its number returned; otherwise the returned version is 0 and the change stays uncommitted, exactly like an ordinary edit of the files.

func (*Service) ApplySystem

func (s *Service) ApplySystem(ctx context.Context, artifactID string) (ApplyResult, error)

ApplySystem links the design-system stylesheet into an artifact's entry document and audits the artifact for values a token already covers.

func (*Service) Checkout

func (s *Service) Checkout(ctx context.Context, artifactID string, number int) error

Checkout restores the artifact directory to a previous version. The revert is scoped to that directory, so unrelated files are never touched, and the current state is snapshotted first by RevertScoped.

func (*Service) CommitVersion

func (s *Service) CommitVersion(ctx context.Context, artifactID, summary string) (Version, error)

CommitVersion snapshots the artifact directory and records it as the next version. The manifest is rewritten so a checkout of the directory alone still reports the right version number.

func (*Service) ContractPath

func (s *Service) ContractPath() string

ContractPath returns the absolute path of DESIGN.md.

func (*Service) Create

func (s *Service) Create(ctx context.Context, p CreateParams) (Artifact, error)

Create materialises a new artifact: directory, seed files, manifest, metadata row and version 1 (a scoped snapshot of the fresh directory).

func (*Service) Critique

func (s *Service) Critique(ctx context.Context, artifactID string, opts CritiqueOptions) (CritiqueReport, error)

Critique runs a quality pass over an artifact: render it, run every deterministic rule, fold in whatever judgement the caller brings, score it, store it and decide whether the loop should go round again.

func (*Service) CritiqueSettingsFor

func (s *Service) CritiqueSettingsFor(skillID string) CritiqueSettings

CritiqueSettingsFor resolves the bounds for one artifact: the configured defaults, overridden by the skill's own od.critique.policy when the artifact was built from a template that declares one.

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, artifactID string) error

Delete drops the metadata of an artifact. The files stay on disk: they belong to the user's repository, and removing them is an explicit, separate act.

func (*Service) Diff

func (s *Service) Diff(ctx context.Context, artifactID string, from, to int) ([]snapshot.DiffEntry, error)

Diff compares two versions of an artifact.

func (*Service) Export

func (s *Service) Export(ctx context.Context, artifactID string, opts ExportOptions) (ExportResult, error)

Export writes the artifact out in one of the supported formats. HTML exports are self-contained single files: local stylesheets, scripts and images are inlined so the result can be mailed or committed on its own.

func (*Service) ExtractSystem

func (s *Service) ExtractSystem(ctx context.Context, opts ExtractOptions) (ExtractResult, error)

ExtractSystem runs the extractor named by opts and returns the resulting system without writing anything. Persisting is a separate, explicit step.

func (*Service) Get

func (s *Service) Get(ctx context.Context, id string) (Artifact, error)

Get returns one artifact.

func (*Service) Inspect

func (s *Service) Inspect(ctx context.Context, artifactID string, version int, opts InspectOptions) (InspectResult, error)

Inspect returns a filtered, paged view of a version's stored index. Pass version 0 for the artifact's current version.

func (*Service) LatestCritique

func (s *Service) LatestCritique(ctx context.Context, artifactID string, version int) (Critique, error)

LatestCritique returns the most recent pass over a version, or ErrNotFound when the version has never been critiqued. Pass version 0 for the current version.

func (*Service) Layout

func (s *Service) Layout() Layout

Layout exposes the resolved directory layout.

func (*Service) List

func (s *Service) List(ctx context.Context, sessionOnly bool) ([]Artifact, error)

List returns artifacts newest first. Pass sessionOnly to restrict the result to the current session.

func (*Service) LiveURL

func (s *Service) LiveURL(ctx context.Context, artifactID string, slide int) (Presentation, error)

LiveURL resolves how to show an artifact, starting a loopback preview server first when this process has none.

It is the call every "open it" path goes through — the CLI, the TUI `o` key, the ACP resource link and the design_present tool — so that they all agree on when a listener is allowed to come into existence: on an explicit request to show something, never as a side effect of rendering or listing.

A preview server that cannot start is not fatal: the returned Presentation still carries the file:// address, which is enough for a local browser.

func (*Service) LoadSystem

func (s *Service) LoadSystem() (DesignSystem, bool, error)

LoadSystem reads the design system, returning the default when none exists yet so callers never have to special-case a fresh project.

func (*Service) MirrorSystem

func (s *Service) MirrorSystem(ctx context.Context, ds DesignSystem, source ExtractSource, target string) (string, error)

MirrorSystem writes the design system to the knowledge base. It returns the document path so a caller can report where it went, and an empty path when no mirror is wired.

func (*Service) Node

func (s *Service) Node(ctx context.Context, artifactID string, version int, nodeID string) (Node, error)

Node resolves one indexed node, which is what a design://<node_id> selection from the UI turns into. Pass version 0 for the current version.

func (*Service) Patch

func (s *Service) Patch(ctx context.Context, artifactID string, ops []PatchOp, summary string, commit bool) (*PatchPlan, int, error)

Patch is the one-shot convenience path used by the CLI and tests: prepare, write, optionally commit. The tool layer uses PreparePatch/ApplyPatchPlan so it can gate the write on a permission prompt showing the diff.

func (*Service) PreparePatch

func (s *Service) PreparePatch(ctx context.Context, artifactID string, ops []PatchOp) (*PatchPlan, error)

PreparePatch resolves patch operations against the artifact's source files and returns the rewritten content without writing anything.

Operations addressed by node_id are resolved through the node index of the artifact's current version, which is what turns a UI selection (design://<node_id>) into a source edit.

func (*Service) Presentation

func (s *Service) Presentation(ctx context.Context, artifactID string, slide int, nodeID string) (Presentation, error)

Presentation resolves how an artifact should be shown. A node id is validated against the current index so a surface never receives a selection it cannot resolve.

func (*Service) PublishPreview

func (s *Service) PublishPreview(ctx context.Context, artifactID string) (preview.Grant, error)

PublishPreview registers an artifact with the preview server and returns the grant. It is idempotent: re-publishing an artifact keeps its token, so a preview already open in a browser survives every iteration.

func (*Service) Render

func (s *Service) Render(ctx context.Context, artifactID string, opts RenderOptions) (RenderResult, error)

Render renders an artifact and stores the resulting structure index against its current version, so a later Inspect (or a UI selection) resolves without re-rendering.

func (*Service) Renderer

func (s *Service) Renderer() *Renderer

Renderer returns the attached renderer, or nil.

func (*Service) Resolve

func (s *Service) Resolve(ctx context.Context, ref string) (Artifact, error)

Resolve turns a human-typed reference into an artifact. It accepts, in order of precedence, an exact id, an exact slug, an id prefix, and finally a case-insensitive substring of the slug or title. An empty reference selects the most recently updated artifact, which is what "the one I am working on" means at a prompt.

Precedence is ordered rather than scored on purpose: an exact id must never lose to a substring match on some other artifact's title.

func (*Service) SaveSystem

func (s *Service) SaveSystem(ds DesignSystem) (string, string, error)

SaveSystem writes tokens.json, regenerates system.css and refreshes the generated section of DESIGN.md, returning the token and stylesheet paths.

func (*Service) SetSystemTokens

func (s *Service) SetSystemTokens(name string, updates map[string]map[string]string) (DesignSystem, error)

SetSystemTokens merges updates into the design system and persists it. A token whose value is empty is removed, which is how a group is pruned.

func (*Service) Store

func (s *Service) Store() *Store

Store exposes the metadata store for the node index and critiques, which the renderer and the critic loop own.

func (*Service) SystemRelPath

func (s *Service) SystemRelPath(file string) string

SystemRelPath returns the project-relative path of a design-system file, for linking it from an artifact.

func (*Service) Versions

func (s *Service) Versions(ctx context.Context, artifactID string) ([]Version, error)

Versions returns the full history of an artifact, each entry carrying its last critique when there is one.

func (*Service) WithMirror

func (s *Service) WithMirror(m SystemMirror) *Service

WithMirror attaches the knowledge-base mirror used by the design system. It is optional: every surface works without one, they just do not publish what they extract.

func (*Service) WithRenderer

func (s *Service) WithRenderer(r *Renderer) *Service

WithRenderer attaches a renderer to the service. It is optional: everything that does not need a browser (create, versions, checkout, diff) works without one, so a machine with no Chromium keeps a usable Design Studio.

func (*Service) WriteWorkspaceFile

func (s *Service) WriteWorkspaceFile(rel string, data []byte) (string, error)

WriteWorkspaceFile writes generated bytes to a working-directory-relative path, refusing to escape the project. It backs image generation, whose output belongs next to the artifact that uses it rather than in a cache.

type SlideBreak

type SlideBreak struct {
	Index      int     `json:"index"`
	BreakAfter string  `json:"break_after"`
	Height     float64 `json:"height"`
}

SlideBreak reports how one slide behaves under print emulation.

type Snapshotter

type Snapshotter interface {
	CreateScoped(ctx context.Context, sessionID, description, rootDir string) (snapshot.Snapshot, error)
	RevertScoped(ctx context.Context, snapshotID string) error
	Compare(ctx context.Context, snapshotID1, snapshotID2 string) ([]snapshot.DiffEntry, error)
}

Snapshotter is the slice of the snapshot service the design package needs. Only the scoped operations are used: an artifact's history must never be able to restore or delete a file outside its own directory.

type Store

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

Store persists design metadata. The artifact files and their history live on disk (working tree + scoped snapshots); this only holds what is needed to list, resolve and navigate them.

func NewStore

func NewStore(db *sql.DB) *Store

NewStore wraps an open database handle.

func (*Store) AddCritique

func (s *Store) AddCritique(ctx context.Context, c Critique) (Critique, error)

AddCritique records a critic pass over a version.

func (*Store) AddVersion

func (s *Store) AddVersion(ctx context.Context, v Version) error

AddVersion records an iteration and makes it the artifact's current version.

func (*Store) CreateArtifact

func (s *Store) CreateArtifact(ctx context.Context, a Artifact) (Artifact, error)

CreateArtifact inserts a new artifact row.

func (*Store) DeleteArtifact

func (s *Store) DeleteArtifact(ctx context.Context, id string) error

DeleteArtifact removes an artifact and, by cascade, its versions, nodes and critiques. The files on disk are never touched: they belong to the user.

func (*Store) GetArtifact

func (s *Store) GetArtifact(ctx context.Context, id string) (Artifact, error)

GetArtifact returns one artifact by id.

func (*Store) GetArtifactByDir

func (s *Store) GetArtifactByDir(ctx context.Context, dir string) (Artifact, error)

GetArtifactByDir returns the artifact stored at a project-relative directory.

func (*Store) GetNode

func (s *Store) GetNode(ctx context.Context, artifactID string, version int, nodeID string) (Node, error)

GetNode resolves a single node of a version, which is what a design://<id> selection turns into.

func (*Store) GetVersion

func (s *Store) GetVersion(ctx context.Context, artifactID string, number int) (Version, error)

GetVersion returns one version of an artifact.

func (*Store) LatestCritique

func (s *Store) LatestCritique(ctx context.Context, artifactID string, version int) (Critique, error)

LatestCritique returns the most recent critic pass over a version, or ErrNotFound when the version has never been critiqued.

func (*Store) ListArtifacts

func (s *Store) ListArtifacts(ctx context.Context, sessionID string) ([]Artifact, error)

ListArtifacts returns artifacts newest first. An empty sessionID lists all of them: artifacts outlive the session that created them.

func (*Store) ListNodes

func (s *Store) ListNodes(ctx context.Context, artifactID string, version, slide int) ([]Node, error)

ListNodes returns the structure index of one version. A slide >= 0 narrows the result to that slide; pass -1 for every node.

func (*Store) ListVersions

func (s *Store) ListVersions(ctx context.Context, artifactID string) ([]Version, error)

ListVersions returns every version of an artifact, oldest first.

func (*Store) ReplaceNodes

func (s *Store) ReplaceNodes(ctx context.Context, artifactID string, version int, nodes []Node) error

ReplaceNodes swaps the whole structure index of one artifact version. The index is a render product, so it is rebuilt wholesale rather than merged.

func (*Store) SetCurrentVersion

func (s *Store) SetCurrentVersion(ctx context.Context, artifactID string, number int) error

SetCurrentVersion points the artifact at an existing version, as a checkout does. It does not create history.

func (*Store) UpdateArtifact

func (s *Store) UpdateArtifact(ctx context.Context, a Artifact) error

UpdateArtifact persists the mutable fields of an artifact and bumps updated_at.

type SystemFinding

type SystemFinding struct {
	// File is the artifact-relative file the value was found in.
	File string `json:"file"`
	// Line is the 1-indexed line.
	Line int `json:"line"`
	// Property is the CSS property, when the value came from a declaration.
	Property string `json:"property,omitempty"`
	// Value is the literal as written.
	Value string `json:"value"`
	// Token is the custom property that should replace it.
	Token string `json:"token"`
}

SystemFinding is one hardcoded value that a token already covers.

type SystemMirror

type SystemMirror interface {
	AddDocument(ctx context.Context, filePath, content string, metadata map[string]interface{}) error
}

SystemMirror is the slice of the knowledge base the design system needs. It is an interface so internal/design does not depend on the RAG stack, which carries an embedding provider and a database of its own.

type Template

type Template struct {
	Name          string   `json:"name"`
	Description   string   `json:"description"`
	Category      string   `json:"category"`
	Scenario      string   `json:"scenario"`
	ExamplePrompt string   `json:"example_prompt,omitempty"`
	Mode          string   `json:"mode,omitempty"`
	Kind          Kind     `json:"kind,omitempty"`
	Preview       string   `json:"preview,omitempty"`
	Viewport      Viewport `json:"viewport,omitempty"`
	// RequiresSystem is od.design_system.requires: the template expects a
	// committed design system and will otherwise produce a look nobody chose.
	RequiresSystem bool     `json:"requires_system"`
	Craft          []string `json:"craft,omitempty"`
	CritiquePolicy string   `json:"critique_policy,omitempty"`
	// Startable reports whether an artifact can be created from this entry. A
	// craft reference or a workflow bundle is real, useful, and not startable.
	Startable bool `json:"startable"`
	// Source is where the entry came from; Installed reports whether it is
	// present in a skills root, which is what makes it visible to the agent.
	Source    TemplateSource `json:"source"`
	Installed bool           `json:"installed"`
	// SourcePath is set for installed entries so a user can find the file.
	SourcePath string `json:"source_path,omitempty"`
}

Template is one gallery entry: a design skill described by its `od:` block. Third-party bundles reach the gallery through the same struct — Pando speaks the format, it does not ship anyone else's content.

func BundledTemplate

func BundledTemplate(name string) (Template, bool)

BundledTemplate returns one Pando-authored bundle by name.

func BundledTemplates

func BundledTemplates() ([]Template, error)

BundledTemplates returns the Pando-authored bundles, sorted by name.

func Gallery(discovered []Template) []Template

Gallery merges the bundled templates with the design bundles already present in the skill discovery roots. An installed bundle of the same name wins: the user's copy is the one the agent actually reads, so listing ours would show a description that is not in force.

func TemplateFromSkill

func TemplateFromSkill(name, description string, meta *od.Metadata) (Template, bool)

TemplateFromSkill converts a discovered skill into a gallery entry. It reports false for a skill with no `od:` block: an ordinary Claude Code skill is not a design template and must not appear as one. Callers pass the fields rather than the skill itself because the design package must not depend on the skills subsystem, which depends on the tool layer, which depends on this package.

type TemplateSource

type TemplateSource string

TemplateSource says where a gallery entry came from.

const (
	// SourceBundled is a Pando-authored bundle embedded in the binary.
	SourceBundled TemplateSource = "bundled"
	// SourceInstalled is a bundle found in one of the skill discovery roots.
	SourceInstalled TemplateSource = "installed"
)

type Version

type Version struct {
	ArtifactID string    `json:"artifact_id"`
	Number     int       `json:"number"`
	SnapshotID string    `json:"snapshot_id"`
	Summary    string    `json:"summary"`
	Critique   *Critique `json:"critique,omitempty"`
	CreatedAt  time.Time `json:"created_at"`
}

Version is one accepted iteration of an artifact, backed by a scoped snapshot of its directory.

type Viewport

type Viewport struct {
	W int `json:"w"`
	H int `json:"h"`
}

Viewport is the render size a preview and screenshots default to.

Directories

Path Synopsis
Package preview serves design artifacts over HTTP so every surface — the WebUI iframe, a system browser opened from the TUI, a Zed resource link — looks at the same running document instead of a file:// copy.
Package preview serves design artifacts over HTTP so every surface — the WebUI iframe, a system browser opened from the TUI, a Zed resource link — looks at the same running document instead of a file:// copy.

Jump to

Keyboard shortcuts

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