Docs

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 + speaker pair 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

ts
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:

PrimitiveSourceUse for
Headline@amdahl/ui/typographyLarge headers, artifact titles
Subheadline@amdahl/ui/typographySection subtitles
Title@amdahl/ui/typographyCard / panel titles
Display@amdahl/ui/typographyHero text
Body@amdahl/ui/typographyShort prose, max ~280 chars
Lead@amdahl/ui/typographyLead paragraphs
Mono@amdahl/ui/typographyIDs, timestamps, code-style
MonoLabel@amdahl/ui/typographyAll-caps mono labels
Badge@amdahl/ui/data-displayStatuses, enum values
Tag@amdahl/ui/data-displayCategorical labels
TagCloud / TagList@amdahl/ui/data-displayArray of strings as tag cloud
Stat@amdahl/ui/data-displayMetrics, counts, percentages
Avatar@amdahl/ui/data-displayUser images / initials
Mention@amdahl/ui/data-displayUUID -> referenced artifact preview
Citation@amdahl/ui/typographyInline source attribution
Code@amdahl/ui/data-displayCode snippets
MarkdownViewrenderer-package internalLong-form markdown body
JsonViewrenderer-package internalRaw JSON tree fallback
DefinitionList@amdahl/ui/data-displayField/value pairs
AssetCard@amdahl/ui/data-displaySingle artifact card
AssetCardListrenderer-package internalList of artifact cards (one per array item)
QuoteCluster@amdahl/ui/data-display{quote, speaker, ...} block
DiscoveryCard@amdahl/ui/data-displayCitation / source card
Insight@amdahl/ui/data-displayHighlighted callout
DateFieldrenderer-package internalFormatted datetime
Link@amdahl/ui/typographyURL 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 title or name (or the first string field) as title; renders status and schema_version as 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 shapeDefault primitive
z.string().min(1).max(280)Body
z.string() longerMarkdownView
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)

ts
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)

ts
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)

ts
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