Layout hints reference
Layout hints are an optional declarative config attached to a registered artifact type. The frontend AutoRenderer reads them and produces a structured layout from the design library primitives instead of falling back to schema-driven defaults.
Hints live alongside the artifact-type registration. The frontend's @amdahl/artifact-renderers package consumes them. Since they are pure data on the registry, every consumer (LibraryPage, MCP resource catalog) sees the same hints.
Why use hints
The AutoRenderer can render any registered Zod schema without hints, but the result is a schema-walking dump (every field rendered top-to-bottom in declaration order). Hints let you:
- Group related fields into named sections.
- Choose a richer primitive than the default for a given field (e.g. render a
quote+speakerpair as<QuoteCluster>instead of two separate<Body>blocks). - Show child artifacts, edges, or events as inline sections.
- Configure filter chips on multi-item sections.
- Customize the header (title field, badge fields, subtitle field).
- Choose what shows in the right sidebar.
Hints are reviewable in PR diffs (no React drift), schema-validated, and zero-cost to update - change the hints, every renderer of that type updates on next mount.
The shape
interface LayoutHints {
sections?: SectionHint[]
sidebar?: {
show?: ('edges' | 'events' | 'versions' | 'metadata')[]
sections?: SectionHint[]
}
header?: {
titleField?: string // default: 'title' or 'name' or first string field
badges?: string[] // default: ['status', 'schema_version']
subtitleField?: string
}
}
interface SectionHint {
title?: string
fields?: string[] // empty = all fields
source?: 'content' | 'children' | 'edges' | 'events' // default 'content'
childArtifactType?: string // filter for source='children'
edgeType?: string // filter for source='edges'
edgeDirection?: 'from' | 'to' // default 'from'
render: RenderPrimitive | { primitive: RenderPrimitive; props?: Record<string, unknown> }
filterChips?: string[] // expose chip filter on multi-item sections
span?: 'full' | 'half' | 'third' // default 'full'
}RenderPrimitive values
Each value maps to a component from @amdahl/ui or to a renderer-package internal:
| Primitive | Source | Use for |
|---|---|---|
Headline | @amdahl/ui/typography | Large headers, artifact titles |
Subheadline | @amdahl/ui/typography | Section subtitles |
Title | @amdahl/ui/typography | Card / panel titles |
Display | @amdahl/ui/typography | Hero text |
Body | @amdahl/ui/typography | Short prose, max ~280 chars |
Lead | @amdahl/ui/typography | Lead paragraphs |
Mono | @amdahl/ui/typography | IDs, timestamps, code-style |
MonoLabel | @amdahl/ui/typography | All-caps mono labels |
Badge | @amdahl/ui/data-display | Statuses, enum values |
Tag | @amdahl/ui/data-display | Categorical labels |
TagCloud / TagList | @amdahl/ui/data-display | Array of strings as tag cloud |
Stat | @amdahl/ui/data-display | Metrics, counts, percentages |
Avatar | @amdahl/ui/data-display | User images / initials |
Mention | @amdahl/ui/data-display | UUID -> referenced artifact preview |
Citation | @amdahl/ui/typography | Inline source attribution |
Code | @amdahl/ui/data-display | Code snippets |
MarkdownView | renderer-package internal | Long-form markdown body |
JsonView | renderer-package internal | Raw JSON tree fallback |
DefinitionList | @amdahl/ui/data-display | Field/value pairs |
AssetCard | @amdahl/ui/data-display | Single artifact card |
AssetCardList | renderer-package internal | List of artifact cards (one per array item) |
QuoteCluster | @amdahl/ui/data-display | {quote, speaker, ...} block |
DiscoveryCard | @amdahl/ui/data-display | Citation / source card |
Insight | @amdahl/ui/data-display | Highlighted callout |
DateField | renderer-package internal | Formatted datetime |
Link | @amdahl/ui/typography | URL with link styling |
Add new primitives by adding the RenderPrimitive enum value + a thin adapter in packages/artifact-renderers/src/_internal/primitives/. New primitives ship as platform-level changes (one design library file + one enum entry).
Default behavior (no hints)
When layoutHints is absent, the AutoRenderer applies a schema-driven default:
- Header uses field named
titleorname(or the first string field) as title; rendersstatusandschema_versionas badges. - Main column walks the Zod schema in declaration order and picks a primitive per field via the zod-to-primitive mapping.
- Sidebar shows edges + events.
This produces a competent rendering for any registered type. Polish via hints.
Zod-to-primitive mapping
Default primitive picks when no hint overrides them:
| Zod shape | Default primitive |
|---|---|
z.string().min(1).max(280) | Body |
z.string() longer | MarkdownView |
z.string().datetime() | DateField |
z.string().uuid() | Mention |
z.string().url() | Link |
z.enum([...]) | Badge |
z.number() | Stat |
z.boolean() | Badge (Yes/No variant) |
z.array(z.string()) | TagList |
z.array(z.object(...)) | AssetCardList |
z.object(nested) | DefinitionList |
z.literal(...) | Mono |
z.record(), z.any(), z.unknown() | JsonView |
Implementation lives at packages/artifact-renderers/src/_internal/zodToPrimitive.ts.
Worked examples
Customer post (legacy compat type)
layoutHints: {
sections: [
{ title: 'Quote', fields: ['body'], render: 'QuoteCluster' },
{ title: 'Source', fields: ['url', 'published_at', 'author', 'company'], render: 'DefinitionList' },
],
sidebar: { show: ['edges', 'events'] },
}Battlecard (Tier 1 strict)
layoutHints: {
header: {
titleField: 'competitor_name',
badges: ['status'],
subtitleField: 'competitor_url',
},
sections: [
{ title: 'Positioning claims', fields: ['positioning_claims'], render: 'AssetCardList', filterChips: ['evidence_id'] },
{ title: 'Objection handlers', fields: ['objection_handlers'], render: 'DefinitionList' },
],
sidebar: { show: ['edges', 'events'] },
}Wave plan (admin / debug oriented)
layoutHints: {
sections: [
{ title: 'Plan', fields: ['name', 'description'], render: 'DefinitionList' },
{ title: 'Waves', fields: ['waves'], render: 'AssetCardList' },
{ title: 'Dispatches', source: 'children', childArtifactType: 'agent_run', render: 'AssetCardList', filterChips: ['profile_id', 'status'] },
],
sidebar: { show: ['events'] },
}Filter chips
When a section's filterChips array is set, the AutoRenderer generates <DropdownMenu> chips above the section. Each chip maps to a field; clicking filters the displayed items. State is local to the renderer (no URL persistence at this layer; library-level filters use URL params).
Section span
Set span: 'half' or span: 'third' to lay out sections in a multi-column grid. Default 'full' gives one section per row.
See also
- Artifact registry architecture for the registry boundary, origins, edges, events.
- Artifact types catalog for the live list of types and their declared hints.