Elle catalog

The closed vocabulary Elle composes generated sheets from (packages/ui-schema). 30 components. To grow it, see docs/strata/generative-ui.md.

Grid
Side-by-side arrangement of same-shaped blocks: paired charts, two panels. 2 columns unless density demands more. Not for KPI rows — those go in a StatCardGrid.
ui/gridContainer
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "columns": {
      "default": 2,
      "description": "Number of columns (1-4)",
      "type": "integer",
      "minimum": 1,
      "maximum": 4
    },
    "gap": {
      "default": "md",
      "description": "Gap between cells",
      "type": "string",
      "enum": [
        "sm",
        "md",
        "lg"
      ]
    }
  },
  "required": [
    "columns",
    "gap"
  ],
  "additionalProperties": false
}
StatCardGrid
The container for a summary row of KPIs. Put 3-8 StatCards in it; it picks its own column count from the available width and the number of tiles. A StatCard always goes in one of these, never in a Grid and never on its own.
ui/stat-cardContainer
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
StatCard
One KPI: a label, a value, and optionally what it did over a comparison window. Only ever a child of StatCardGrid.
ui/stat-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "description": "Metric label (e.g. 'Total Views')"
    },
    "value": {
      "description": "The metric value; numbers auto-format compactly (1.2M)",
      "type": [
        "string",
        "number",
        "null"
      ]
    },
    "subtitle": {
      "description": "Context line under the value",
      "type": "string"
    },
    "delta": {
      "description": "Percent change vs the previous period (e.g. 23.5)",
      "type": "number"
    },
    "deltaInverted": {
      "description": "Set when DOWN is good (cost, churn)",
      "type": "boolean"
    },
    "comparison": {
      "description": "How many DAYS the window the delta measures against is, rendered as 'vs last 30d'. Set it whenever a delta is set; it is ignored without one.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "label",
    "value"
  ],
  "additionalProperties": false
}
TrendChart
A time series as the house bar chart (hover shows per-point values). One metric per chart.
ui/trend-chart
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "Chart title (e.g. 'Daily Views')"
    },
    "data": {
      "description": "Inline series (small series only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Point label (e.g. a date: 'Jun 4')"
          },
          "value": {
            "type": "number",
            "description": "Point value"
          }
        },
        "required": [
          "label",
          "value"
        ],
        "additionalProperties": false
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "xKey": {
      "description": "Row field for point labels (e.g. 'date'); defaults to 'label'",
      "type": "string"
    },
    "yKey": {
      "description": "Row field for point values (e.g. 'views_count'); defaults to 'value'",
      "type": "string"
    },
    "subtitle": {
      "description": "Period line under the total (e.g. 'Last 30 days')",
      "type": "string"
    },
    "color": {
      "description": "House palette token; omit for the default",
      "type": "string",
      "enum": [
        "chart-1",
        "chart-2",
        "chart-3",
        "chart-4",
        "chart-5"
      ]
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}
DataTable
Ranked or comparable records: posts, campaigns, creators. Declare columns; bind rows to a tool result.
ui/data-table
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "columns": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Row field this column reads; dots reach nested fields ('metrics.views')"
          },
          "label": {
            "type": "string",
            "description": "Column header"
          },
          "align": {
            "description": "Right-align numbers",
            "type": "string",
            "enum": [
              "left",
              "right"
            ]
          },
          "format": {
            "description": "Value formatting: compact = 1.2M, currency = USD, date = locale date",
            "type": "string",
            "enum": [
              "text",
              "number",
              "compact",
              "currency",
              "percent",
              "date"
            ]
          }
        },
        "required": [
          "key",
          "label"
        ],
        "additionalProperties": false
      },
      "description": "Column definitions, in display order"
    },
    "rows": {
      "description": "Inline rows (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {
          "type": [
            "string",
            "number",
            "boolean",
            "null"
          ]
        }
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of row objects inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "maxRows": {
      "description": "Cap the rendered rows",
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "emptyMessage": {
      "description": "Shown when there are no rows",
      "type": "string"
    }
  },
  "required": [
    "columns"
  ],
  "additionalProperties": false
}
Badge
A short status or category chip (Active, Failed, Organic).
ui/badge
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "description": "Badge text"
    },
    "variant": {
      "default": "secondary",
      "description": "Semantic style",
      "type": "string",
      "enum": [
        "default",
        "secondary",
        "destructive",
        "outline",
        "success"
      ]
    }
  },
  "required": [
    "label",
    "variant"
  ],
  "additionalProperties": false
}
Markdown
Prose: explanations, takeaways, caveats. Keep it short — the chat voice carries the narrative.
ui/markdown
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "GitHub-flavored markdown prose (headings, lists, tables)"
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
SectionLabel
A mono uppercase heading that starts a sheet section.
ui/section-label
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "Uppercase section heading (e.g. 'TOP POSTS')"
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
Separator
A horizontal rule between sections.
ui/separator
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
MediaCard
A single piece of social media — one TikTok video or photo slideshow — with its metrics and creator. Hover plays the video / cycles the slides. Give it an action to make it tappable.
ui/live-post-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "media": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ],
      "description": "A video URL, or an array of image URLs for a slideshow"
    },
    "mediaType": {
      "description": "Override the kind; inferred from the URL when omitted (an array is always images)",
      "type": "string",
      "enum": [
        "video",
        "image"
      ]
    },
    "author": {
      "description": "Creator handle or name (e.g. \"@nike\")",
      "type": "string"
    },
    "platform": {
      "description": "Source platform for the corner icon (e.g. 'tiktok', 'instagram')",
      "type": "string"
    },
    "primaryStat": {
      "description": "Headline metric, already formatted (e.g. \"1.2M\")",
      "type": "string"
    },
    "primaryLabel": {
      "description": "Headline metric label (e.g. \"Views\")",
      "type": "string"
    },
    "stats": {
      "description": "Up to ~4 secondary metrics",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Metric label (e.g. \"Likes\")"
          },
          "value": {
            "type": "string",
            "description": "The metric, already formatted (e.g. \"12.4K\")"
          }
        },
        "required": [
          "label",
          "value"
        ],
        "additionalProperties": false
      }
    },
    "action": {
      "description": "What tapping the card does",
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "navigate"
            },
            "to": {
              "type": "string",
              "minLength": 1,
              "description": "In-app destination. Project-relative (no leading slash) is resolved under the current project — e.g. 'create/slideshow-remix', 'social/research'. A leading slash is used verbatim."
            },
            "search": {
              "description": "Query params, e.g. { sourceVideoId: '7123...' }",
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "string"
              }
            },
            "label": {
              "description": "Optional button text (defaults to a sensible verb)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "to"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "message"
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "description": "Sent to Elle as the user's next message when tapped, e.g. 'Make a slideshow from this video'"
            },
            "label": {
              "description": "Optional button text (defaults to the message)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "additionalProperties": false
        }
      ]
    }
  },
  "required": [
    "media"
  ],
  "additionalProperties": false
}
MediaGrid
A gallery of social posts (inspiration, trending clips, a content portfolio) bound to a tool result via dataPath. Each item renders as a media card; give it an action and tapping a card navigates or messages Elle (e.g. "remix this"). This is the surface for "suggest videos to make content from".
ui/live-post-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "items": {
      "description": "Inline items (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of media items (e.g. /toolResults/getInspiration/items) inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "columns": {
      "description": "Fixed column count; omit for a responsive auto-fit grid",
      "type": "integer",
      "minimum": 1,
      "maximum": 3
    },
    "maxItems": {
      "description": "Cap the rendered cards",
      "type": "integer",
      "minimum": 1,
      "maximum": 24
    },
    "mediaKey": {
      "description": "Item field with the media URL or slide-URL array (default 'media')",
      "type": "string"
    },
    "mediaTypeKey": {
      "description": "Item field with 'video'|'image' (default 'mediaType')",
      "type": "string"
    },
    "authorKey": {
      "description": "Item field for the author handle (default 'author')",
      "type": "string"
    },
    "platformKey": {
      "description": "Item field for the platform (default 'platform')",
      "type": "string"
    },
    "primaryStat": {
      "description": "Headline metric per card (default reads 'views')",
      "type": "object",
      "properties": {
        "key": {
          "type": "string",
          "description": "Item field this stat reads; dots reach nested fields"
        },
        "label": {
          "type": "string",
          "description": "Stat label"
        },
        "format": {
          "description": "Value formatting (default compact for numbers)",
          "type": "string",
          "enum": [
            "text",
            "number",
            "compact",
            "currency",
            "percent",
            "date"
          ]
        }
      },
      "required": [
        "key",
        "label"
      ],
      "additionalProperties": false
    },
    "stats": {
      "description": "Secondary metric rows (default likes/comments/shares)",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Item field this stat reads; dots reach nested fields"
          },
          "label": {
            "type": "string",
            "description": "Stat label"
          },
          "format": {
            "description": "Value formatting (default compact for numbers)",
            "type": "string",
            "enum": [
              "text",
              "number",
              "compact",
              "currency",
              "percent",
              "date"
            ]
          }
        },
        "required": [
          "key",
          "label"
        ],
        "additionalProperties": false
      }
    },
    "action": {
      "description": "What tapping a card does",
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "navigate",
            "message"
          ],
          "description": "navigate to a page, or message Elle"
        },
        "to": {
          "description": "navigate: in-app destination, project-relative (e.g. 'create/slideshow-remix'). Required for kind=navigate.",
          "type": "string"
        },
        "search": {
          "description": "navigate: static query params shared by every card",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "searchFromKeys": {
          "description": "navigate: query-param name → item field key, filled per card (e.g. { sourceVideoId: 'videoId' })",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "textTemplate": {
          "description": "message: the text sent to Elle, with {field} placeholders filled from the item (e.g. 'Make a slideshow from {videoId}'). Required for kind=message.",
          "type": "string"
        },
        "label": {
          "description": "Optional affordance label",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "display": {
      "description": "Card treatment: 'post' (default) renders the social-post card (9:16 media pane + stats); 'plain' renders a bare image/video card at its natural aspect ratio — use for ad previews and any media that isn't a social post",
      "type": "string",
      "enum": [
        "post",
        "plain"
      ]
    },
    "emptyMessage": {
      "description": "Shown when there are no items",
      "type": "string"
    }
  },
  "additionalProperties": false
}
SelectList
Pick SEVERAL options and commit once (checkbox rows + one Save) — keeping a subset of generated hooks, choosing accounts, pruning a list. Save sends your messageTemplate back to you with the selected labels substituted for the literal token {items}, which your template MUST contain; a template missing it is rejected, because a lost selection makes you ask again. Rows always start UNCHECKED and there is no way to pre-check them — the user opts options IN. Save is pinned in the sheet footer, so a long list stays committable without scrolling. Nothing commits before Save. For a single choice use TrackList (music) or an action-bearing List; for consequence-tier questions use the decision dock.
ui/select-list
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "items": {
      "description": "Inline items (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of selectable items (e.g. /toolResults/getHookSuggestions/hooks) inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "maxItems": {
      "description": "Cap the rendered rows (default and hard ceiling 20)",
      "type": "integer",
      "minimum": 1,
      "maximum": 20
    },
    "idKey": {
      "description": "Item field with a stable id (default 'id')",
      "type": "string"
    },
    "labelKey": {
      "description": "Item field with the row label (default 'label')",
      "type": "string"
    },
    "sublabelKey": {
      "description": "Item field with a secondary line (optional)",
      "type": "string"
    },
    "minSelected": {
      "description": "Save disables below this many selections (default 1)",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "maxSelected": {
      "description": "Cap on selections",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "saveLabel": {
      "description": "Save button label (default 'Save')",
      "type": "string",
      "maxLength": 40
    },
    "messageTemplate": {
      "type": "string",
      "minLength": 1,
      "description": "The message Save sends you. It MUST contain the literal token {items}, which is replaced by the selected labels as a dashed list (e.g. \"Save these hooks to the experiment:\\n{items}\"). The token is spelled {items} — not {selected}, not {hooks}. Selection commits ONCE, through this message, so a template without the token loses the answer silently."
    },
    "emptyMessage": {
      "description": "Shown when there are no items",
      "type": "string"
    }
  },
  "required": [
    "messageTemplate"
  ],
  "additionalProperties": false
}
TrackList
A music picker with AUDIO preview (MediaCard only plays video) — trending tracks bound to a tool result via dataPath (e.g. /toolResults/getTrendingTracks/tracks). One row previews at a time; give it a message action and choosing a row sends the pick back to you (thread the chosen url/title/author into the next compositor call).
ui/track-list
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "tracks": {
      "description": "Inline track items (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of track items (e.g. /toolResults/getTrendingTracks/tracks) inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "maxItems": {
      "description": "Cap the rendered rows",
      "type": "integer",
      "minimum": 1,
      "maximum": 20
    },
    "idKey": {
      "description": "Item field with a stable id (default 'id')",
      "type": "string"
    },
    "titleKey": {
      "description": "Item field with the track title (default 'title')",
      "type": "string"
    },
    "authorKey": {
      "description": "Item field with the artist (default 'author')",
      "type": "string"
    },
    "coverKey": {
      "description": "Item field with cover art (default 'coverUrl')",
      "type": "string"
    },
    "previewKey": {
      "description": "Item field with the audio URL the preview plays (default 'previewUrl')",
      "type": "string"
    },
    "durationKey": {
      "description": "Item field with the duration in seconds (default 'durationSec')",
      "type": "string"
    },
    "action": {
      "description": "What choosing a row does — use kind=message with {field} placeholders (e.g. 'Use the track {title} by {author}: {previewUrl}')",
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "navigate",
            "message"
          ],
          "description": "navigate to a page, or message Elle"
        },
        "to": {
          "description": "navigate: in-app destination, project-relative (e.g. 'create/slideshow-remix'). Required for kind=navigate.",
          "type": "string"
        },
        "search": {
          "description": "navigate: static query params shared by every card",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "searchFromKeys": {
          "description": "navigate: query-param name → item field key, filled per card (e.g. { sourceVideoId: 'videoId' })",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "textTemplate": {
          "description": "message: the text sent to Elle, with {field} placeholders filled from the item (e.g. 'Make a slideshow from {videoId}'). Required for kind=message.",
          "type": "string"
        },
        "label": {
          "description": "Optional affordance label",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "chooseLabel": {
      "description": "The choose affordance label (default 'Use this track')",
      "type": "string"
    },
    "emptyMessage": {
      "description": "Shown when there are no tracks",
      "type": "string"
    }
  },
  "additionalProperties": false
}
Heading
A prose heading for a prominent sheet or section title. Use SectionLabel for a mono uppercase divider; use this for a real title.
ui/heading
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "Heading text"
    },
    "variant": {
      "default": "heading",
      "description": "Type scale: display = hero, title = section, heading = sheet title, subhead = pane header",
      "type": "string",
      "enum": [
        "display",
        "title",
        "heading",
        "subhead"
      ]
    }
  },
  "required": [
    "text",
    "variant"
  ],
  "additionalProperties": false
}
Callout
A bordered banner for the one thing that should not be missed — a key insight, a recommendation, or a warning (variant destructive). Not for ordinary prose (use Markdown).
ui/alert
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "The message body"
    },
    "title": {
      "description": "Optional bold lead line",
      "type": "string"
    },
    "variant": {
      "default": "default",
      "description": "'destructive' for warnings/errors; 'default' otherwise",
      "type": "string",
      "enum": [
        "default",
        "destructive"
      ]
    },
    "icon": {
      "description": "Lucide icon name in kebab-case (e.g. 'sparkles', 'flame')",
      "type": "string"
    }
  },
  "required": [
    "text",
    "variant"
  ],
  "additionalProperties": false
}
Button
A standalone call-to-action. Tapping runs its action (navigate to a page, or message you). Use for the next step at the end of a sheet ("Open the builder", "Schedule these").
ui/button
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "description": "Button text"
    },
    "action": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "navigate"
            },
            "to": {
              "type": "string",
              "minLength": 1,
              "description": "In-app destination. Project-relative (no leading slash) is resolved under the current project — e.g. 'create/slideshow-remix', 'social/research'. A leading slash is used verbatim."
            },
            "search": {
              "description": "Query params, e.g. { sourceVideoId: '7123...' }",
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "string"
              }
            },
            "label": {
              "description": "Optional button text (defaults to a sensible verb)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "to"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "message"
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "description": "Sent to Elle as the user's next message when tapped, e.g. 'Make a slideshow from this video'"
            },
            "label": {
              "description": "Optional button text (defaults to the message)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "additionalProperties": false
        }
      ],
      "description": "What tapping the button does (navigate or message Elle)"
    },
    "variant": {
      "default": "default",
      "description": "Visual weight; use default for the primary action",
      "type": "string",
      "enum": [
        "default",
        "outline",
        "secondary",
        "ghost",
        "destructive"
      ]
    },
    "icon": {
      "description": "Lucide icon name in kebab-case",
      "type": "string"
    }
  },
  "required": [
    "label",
    "action",
    "variant"
  ],
  "additionalProperties": false
}
Card
A titled, bordered container that groups related elements into one block (e.g. a strategy summary, a single record's details). A container — nest its children by id.
ui/cardContainer
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "description": "Card title",
      "type": "string"
    },
    "subtitle": {
      "description": "Supporting line under the title",
      "type": "string"
    }
  },
  "additionalProperties": false
}
List
A compact, non-tabular list of records (accounts, campaigns, steps, options) bound to a tool result via dataPath — title + optional subtitle + status chip per row. Give it an action to make rows tappable. Prefer DataTable when columns of metrics matter; use List for lighter, scannable rows.
ui/list-row
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "items": {
      "description": "Inline rows (small sets only — prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of row objects inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "titleKey": {
      "description": "Item field for the row title (default 'title')",
      "type": "string"
    },
    "subtitleKey": {
      "description": "Item field for the secondary line (default 'subtitle')",
      "type": "string"
    },
    "badgeKey": {
      "description": "Item field for a right-aligned status chip",
      "type": "string"
    },
    "maxItems": {
      "description": "Cap the rendered rows",
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "action": {
      "description": "What tapping a row does",
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "navigate",
            "message"
          ],
          "description": "navigate to a page, or message Elle"
        },
        "to": {
          "description": "navigate: in-app destination, project-relative (e.g. 'create/slideshow-remix'). Required for kind=navigate.",
          "type": "string"
        },
        "search": {
          "description": "navigate: static query params shared by every card",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "searchFromKeys": {
          "description": "navigate: query-param name → item field key, filled per card (e.g. { sourceVideoId: 'videoId' })",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "textTemplate": {
          "description": "message: the text sent to Elle, with {field} placeholders filled from the item (e.g. 'Make a slideshow from {videoId}'). Required for kind=message.",
          "type": "string"
        },
        "label": {
          "description": "Optional affordance label",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "emptyMessage": {
      "description": "Shown when there are no rows",
      "type": "string"
    }
  },
  "additionalProperties": false
}
Progress
A horizontal completion bar (0–100) — goal progress, pipeline completion, budget used. Pair with a label.
ui/progress
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "value": {
      "type": "number",
      "description": "Completion 0–100"
    },
    "label": {
      "description": "Label shown above the bar (with the % on the right)",
      "type": "string"
    }
  },
  "required": [
    "value"
  ],
  "additionalProperties": false
}
RateChart
A percentage-rate time series as a compact area chart — engagement rate, CTR, conversion. Values are percents (4.8 = 4.8%). Raw counts use TrendChart.
ui/rate-chart
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "Chart title (e.g. 'Engagement Rate')"
    },
    "data": {
      "description": "Inline series (small series only — prefer dataPath). Values are percents: 4.8 = 4.8%",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Point label (e.g. a date: 'Jun 4')"
          },
          "value": {
            "type": "number",
            "description": "Point value"
          }
        },
        "required": [
          "label",
          "value"
        ],
        "additionalProperties": false
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "xKey": {
      "description": "Row field for point labels (e.g. 'date'); defaults to 'label'",
      "type": "string"
    },
    "yKey": {
      "description": "Row field for point values (e.g. 'engagement_rate'); defaults to 'value'",
      "type": "string"
    },
    "subtitle": {
      "description": "Period line under the title (e.g. 'Last 30 days')",
      "type": "string"
    },
    "color": {
      "description": "House palette token; omit for the default",
      "type": "string",
      "enum": [
        "chart-1",
        "chart-2",
        "chart-3",
        "chart-4",
        "chart-5"
      ]
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}
PaceChart
Goal progress against an on-track pace line: the measured series, a dashed reference to the target, and the target itself. The "am I on track" chart — one metric, same units throughout. variant manual renders the honest no-plot state for a manually tracked goal.
ui/pace-chart
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "Chart title (e.g. 'Subscribers')"
    },
    "subtitle": {
      "description": "Period line under the title (e.g. 'Day 12 of 30')",
      "type": "string"
    },
    "target": {
      "type": "number",
      "description": "The goal target, in the same units as the series"
    },
    "horizonDays": {
      "description": "Total days in the goal window (Day 1 → the deadline). REQUIRED with the measured variant: the derived pace line reaches the target at the deadline, not at the last measured point (the series is assumed to start on Day 1). Rows carrying their own pace values still win.",
      "type": "integer",
      "minimum": 1,
      "maximum": 365
    },
    "baseline": {
      "description": "The metric's Day-1 value, anchoring the derived pace line (defaults to the first measured value)",
      "type": "number"
    },
    "data": {
      "description": "Inline measured series (prefer dataPath)",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "ISO date (YYYY-MM-DD)"
          },
          "value": {
            "type": "number",
            "description": "The measured value on that day"
          },
          "pace": {
            "description": "The on-track reference for that day (same units); omit to derive a straight baseline→target line",
            "type": "number"
          }
        },
        "required": [
          "date",
          "value"
        ],
        "additionalProperties": false
      }
    },
    "dataPath": {
      "type": "string",
      "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
      "description": "JSON Pointer to an array of {date, value} points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
    },
    "dateKey": {
      "description": "Row field for the ISO date (default 'date')",
      "type": "string"
    },
    "valueKey": {
      "description": "Row field for the measured value (default 'value')",
      "type": "string"
    },
    "paceKey": {
      "description": "Row field with a precomputed pace value (default 'pace'); rows without one take the derived baseline→target line",
      "type": "string"
    },
    "variant": {
      "default": "measured",
      "description": "'manual' renders the honest no-plot state for a manually tracked goal (current value + target, never a fabricated series)",
      "type": "string",
      "enum": [
        "measured",
        "manual"
      ]
    },
    "currentValue": {
      "description": "manual: the current tracked value (null renders the — placeholder)",
      "type": [
        "number",
        "null"
      ]
    },
    "manualNote": {
      "description": "manual: the mono note (default 'Tracked manually')",
      "type": "string"
    }
  },
  "required": [
    "title",
    "target",
    "variant"
  ],
  "additionalProperties": false
}
MeterList
Labeled x-of-y progress rows (icon, label, count, track, optional next-step meta line) — the per-workstream meters of a dashboard panel. A single standalone bar is Progress.
ui/meter-list
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "items": {
      "minItems": 1,
      "maxItems": 12,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Row label (e.g. a workstream: 'Content', 'Ads')"
          },
          "value": {
            "type": "number",
            "description": "Completed count"
          },
          "max": {
            "type": "number",
            "description": "Total count"
          },
          "valueLabel": {
            "description": "Overrides the default \"x of y\" text — only when the counts alone would mislead",
            "type": "string"
          },
          "meta": {
            "description": "One muted line under the track — the group's next step, not a restated label",
            "type": "string"
          },
          "icon": {
            "description": "Lucide icon name in kebab-case (e.g. 'pencil-line', 'megaphone')",
            "type": "string"
          }
        },
        "required": [
          "label",
          "value",
          "max"
        ],
        "additionalProperties": false
      },
      "description": "The meter rows, in display order"
    }
  },
  "required": [
    "items"
  ],
  "additionalProperties": false
}
LivePostDetail
The full deep-dive on ONE post: a metrics grid, views + engagement charts, and audience breakdowns beside a sticky 9:16 media preview. The sheet for "how did this post do". Inline the metric scalars; bind chart series via dataPath when a tool returned them.
ui/live-post-detail
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "media": {
      "description": "A video URL, or an array of image URLs for a slideshow",
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "mediaType": {
      "description": "Override the kind; inferred from the URL when omitted",
      "type": "string",
      "enum": [
        "video",
        "image"
      ]
    },
    "metrics": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Metric label (e.g. 'Views')"
          },
          "value": {
            "description": "The value; numbers format compactly (1.2M), strings pass through (\"8.2%\"), null renders —",
            "type": [
              "string",
              "number",
              "null"
            ]
          },
          "tooltip": {
            "description": "What the metric counts",
            "type": "string"
          }
        },
        "required": [
          "label",
          "value"
        ],
        "additionalProperties": false
      },
      "description": "The stat grid (Views, Eng. Rate, Likes, …) — inline the scalars"
    },
    "viewsDaily": {
      "description": "Views per day (last 30 days)",
      "type": "object",
      "properties": {
        "data": {
          "description": "Inline points (prefer dataPath)",
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "description": "Point label (e.g. a date: 'Jun 4')"
              },
              "value": {
                "type": "number",
                "description": "Point value"
              }
            },
            "required": [
              "label",
              "value"
            ],
            "additionalProperties": false
          }
        },
        "dataPath": {
          "type": "string",
          "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
          "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
        },
        "xKey": {
          "description": "Row field for point labels (default 'label')",
          "type": "string"
        },
        "yKey": {
          "description": "Row field for point values (default 'value')",
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "viewsHourly": {
      "description": "Views per hour (last 24 hours)",
      "type": "object",
      "properties": {
        "data": {
          "description": "Inline points (prefer dataPath)",
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "description": "Point label (e.g. a date: 'Jun 4')"
              },
              "value": {
                "type": "number",
                "description": "Point value"
              }
            },
            "required": [
              "label",
              "value"
            ],
            "additionalProperties": false
          }
        },
        "dataPath": {
          "type": "string",
          "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
          "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
        },
        "xKey": {
          "description": "Row field for point labels (default 'label')",
          "type": "string"
        },
        "yKey": {
          "description": "Row field for point values (default 'value')",
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "engagementDaily": {
      "description": "Engagement rate (%) per day (last 30 days)",
      "type": "object",
      "properties": {
        "data": {
          "description": "Inline points (prefer dataPath)",
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "description": "Point label (e.g. a date: 'Jun 4')"
              },
              "value": {
                "type": "number",
                "description": "Point value"
              }
            },
            "required": [
              "label",
              "value"
            ],
            "additionalProperties": false
          }
        },
        "dataPath": {
          "type": "string",
          "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
          "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
        },
        "xKey": {
          "description": "Row field for point labels (default 'label')",
          "type": "string"
        },
        "yKey": {
          "description": "Row field for point values (default 'value')",
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "engagementHourly": {
      "description": "Engagement rate (%) per hour (last 24 hours)",
      "type": "object",
      "properties": {
        "data": {
          "description": "Inline points (prefer dataPath)",
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "description": "Point label (e.g. a date: 'Jun 4')"
              },
              "value": {
                "type": "number",
                "description": "Point value"
              }
            },
            "required": [
              "label",
              "value"
            ],
            "additionalProperties": false
          }
        },
        "dataPath": {
          "type": "string",
          "pattern": "^\\/toolResults\\/[A-Za-z0-9_-]+(?:\\/[^/~]+)*$",
          "description": "JSON Pointer to an array of data points inside a tool result you already received, e.g. \"/toolResults/getDailyMetrics/data\". Prefer this over inlining large arrays."
        },
        "xKey": {
          "description": "Row field for point labels (default 'label')",
          "type": "string"
        },
        "yKey": {
          "description": "Row field for point values (default 'value')",
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "trafficSources": {
      "description": "Where views came from (For You, Profile, Search, …)",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Row label (e.g. 'For You', 'United States')"
          },
          "percentage": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "0–1 fraction of the whole (0.62 = 62%)"
          }
        },
        "required": [
          "label",
          "percentage"
        ],
        "additionalProperties": false
      }
    },
    "viewerTypes": {
      "description": "Follower vs non-follower split",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Row label (e.g. 'For You', 'United States')"
          },
          "percentage": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "0–1 fraction of the whole (0.62 = 62%)"
          }
        },
        "required": [
          "label",
          "percentage"
        ],
        "additionalProperties": false
      }
    },
    "countries": {
      "description": "Top viewer countries",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Row label (e.g. 'For You', 'United States')"
          },
          "percentage": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "0–1 fraction of the whole (0.62 = 62%)"
          }
        },
        "required": [
          "label",
          "percentage"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "metrics"
  ],
  "additionalProperties": false
}
AccountIdentity
The one way to name a connected social account: avatar, platform badge, and username in a row. Lead a per-account section or Card with it instead of writing a bare handle.
ui/account-identity
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "username": {
      "type": "string",
      "description": "The account handle, without the @"
    },
    "platform": {
      "type": "string",
      "enum": [
        "tiktok",
        "instagram",
        "linkedin",
        "youtube",
        "facebook"
      ],
      "description": "The connected platform (renders the platform badge)"
    },
    "avatarUrl": {
      "description": "Profile picture URL; omit to fall back to the platform mark",
      "type": "string"
    },
    "size": {
      "default": "md",
      "description": "Row scale",
      "type": "string",
      "enum": [
        "sm",
        "md",
        "lg"
      ]
    }
  },
  "required": [
    "username",
    "platform",
    "size"
  ],
  "additionalProperties": false
}
AppIdentity
The one way to name an ad-target app: store artwork, an App Store/Google Play badge, and the app name in a row. Any ads surface naming an iOS or Android app renders this.
ui/app-identity
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The app name as the store lists it"
    },
    "platform": {
      "type": "string",
      "enum": [
        "ios",
        "android"
      ],
      "description": "Store platform — picks the App Store / Google Play badge"
    },
    "iconUrl": {
      "description": "Store icon artwork URL",
      "type": "string"
    },
    "size": {
      "default": "md",
      "description": "Row scale",
      "type": "string",
      "enum": [
        "sm",
        "md",
        "lg"
      ]
    }
  },
  "required": [
    "name",
    "platform",
    "size"
  ],
  "additionalProperties": false
}
CatalogCard
A square-thumbnail picker card (title + optional description) for non-social items: products, references, templates. Give it an action so choosing it commits. Social posts use MediaCard/MediaGrid.
ui/catalog-card
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "Item name"
    },
    "description": {
      "description": "One supporting line under the title",
      "type": "string"
    },
    "thumbnailUrl": {
      "description": "Square thumbnail image URL",
      "type": "string"
    },
    "icon": {
      "description": "Lucide icon name in kebab-case, shown when there is no thumbnail",
      "type": "string"
    },
    "action": {
      "description": "What choosing the card does",
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "navigate"
            },
            "to": {
              "type": "string",
              "minLength": 1,
              "description": "In-app destination. Project-relative (no leading slash) is resolved under the current project — e.g. 'create/slideshow-remix', 'social/research'. A leading slash is used verbatim."
            },
            "search": {
              "description": "Query params, e.g. { sourceVideoId: '7123...' }",
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "string"
              }
            },
            "label": {
              "description": "Optional button text (defaults to a sensible verb)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "to"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "message"
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "description": "Sent to Elle as the user's next message when tapped, e.g. 'Make a slideshow from this video'"
            },
            "label": {
              "description": "Optional button text (defaults to the message)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "additionalProperties": false
        }
      ]
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}
EmptyState
The honest zero-state for a section with nothing to show: names what belongs here and offers one action to fill it. Use it instead of an empty table or invented placeholder data.
ui/empty-state
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "What belongs here (e.g. 'No posts scheduled yet')"
    },
    "description": {
      "description": "One line on how it gets filled",
      "type": "string"
    },
    "icon": {
      "description": "Lucide icon name in kebab-case for the medallion",
      "type": "string"
    },
    "variant": {
      "default": "section",
      "description": "'inline' for a small slot inside a dense composition",
      "type": "string",
      "enum": [
        "section",
        "inline"
      ]
    },
    "action": {
      "description": "One way to fill the space (give it a label)",
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "navigate"
            },
            "to": {
              "type": "string",
              "minLength": 1,
              "description": "In-app destination. Project-relative (no leading slash) is resolved under the current project — e.g. 'create/slideshow-remix', 'social/research'. A leading slash is used verbatim."
            },
            "search": {
              "description": "Query params, e.g. { sourceVideoId: '7123...' }",
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "string"
              }
            },
            "label": {
              "description": "Optional button text (defaults to a sensible verb)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "to"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "kind": {
              "type": "string",
              "const": "message"
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "description": "Sent to Elle as the user's next message when tapped, e.g. 'Make a slideshow from this video'"
            },
            "label": {
              "description": "Optional button text (defaults to the message)",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "additionalProperties": false
        }
      ]
    }
  },
  "required": [
    "title",
    "variant"
  ],
  "additionalProperties": false
}
OutcomeCard
The authoritative record of what an action actually did — created, submitted, unconfirmed (warning), failed (destructive). Use it when the result must stay visible regardless of narration. Page notices are Callout; ordinary prose is Markdown.
ui/alert
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "tone": {
      "default": "neutral",
      "description": "The outcome itself: success = it happened, warning = unconfirmed/pending, destructive = it failed",
      "type": "string",
      "enum": [
        "neutral",
        "success",
        "warning",
        "destructive"
      ]
    },
    "title": {
      "type": "string",
      "description": "What actually happened (e.g. 'Campaign created — 4 ad sets')"
    },
    "details": {
      "description": "Supporting lines: ids, counts, what to check next. An ARRAY with one string per line — not a single joined string, and not newline-separated",
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "tone",
    "title"
  ],
  "additionalProperties": false
}
CodeBlock
A copyable snippet block for text the user takes elsewhere: generated prompts, captions, hook scripts, API examples. One block, one snippet; the copy button is built in.
ui/code-block
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "description": "The snippet body — copied verbatim by the copy button"
    },
    "language": {
      "description": "Short language tag shown as a header badge (e.g. 'ts', 'swift')",
      "type": "string"
    },
    "filename": {
      "description": "Filename/label for the header row",
      "type": "string"
    },
    "highlight": {
      "default": false,
      "description": "Syntax-highlight as JS/TS code. Leave false for prose (prompts, captions, hooks) — it would mis-tokenize",
      "type": "boolean"
    },
    "lineNumbers": {
      "description": "Force the line-number gutter on/off (default: on for multi-line snippets)",
      "type": "boolean"
    }
  },
  "required": [
    "code",
    "highlight"
  ],
  "additionalProperties": false
}
AgentBadge
The mono uppercase chip attributing work to an agent or area, tinted with the area accent. One or two words, on task rows, activity digests, deliverable cards.
ui/agent-badge
Props schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "maxLength": 24,
      "description": "The agent or area name (e.g. 'UGC Researcher')"
    },
    "area": {
      "default": "neutral",
      "description": "Tints the chip with the area accent",
      "type": "string",
      "enum": [
        "social",
        "ads",
        "ugc",
        "measurement",
        "neutral"
      ]
    },
    "dot": {
      "description": "Render as a small dot + mono label instead of a bordered chip",
      "type": "boolean"
    }
  },
  "required": [
    "label",
    "area"
  ],
  "additionalProperties": false
}