{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openpresentation.org/schema/opf-layout/v1",
  "title": "Slide Layout",
  "description": "Schema for slide-layout records in the pptx.gallery library. Each record describes a semantic slide layout — what regions it exposes and what content kinds those regions are intended to hold. Layouts are referenced from OPF documents via Slide.layout; the engine resolves the reference against catalogs.layouts (inline) → catalogs.layouts.source → the default catalog at https://www.pptx.gallery/layouts. Free-form custom layout names that don't resolve through any catalog fall through to engine-defined layouts.",
  "type": "object",
  "required": [
    "$schema",
    "id",
    "name"
  ],
  "properties": {
    "$schema": {
      "type": "string",
      "const": "https://openpresentation.org/schema/opf-layout/v1",
      "description": "Identifies this record as a slide layout in the openpresentation.org catalog."
    },
    "id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]*$",
      "description": "Stable slug used by OPF documents to reference this layout via Slide.layout. Lowercase kebab-case.",
      "examples": [
        "title",
        "title-subtitle",
        "chart-1x",
        "image-bleed"
      ]
    },
    "name": {
      "type": "string",
      "description": "Human-readable layout name shown in layout pickers.",
      "examples": [
        "Title",
        "Title Subtitle",
        "Chart 1x",
        "Image Bleed"
      ]
    },
    "summary": {
      "type": "string",
      "description": "One-sentence positioning of the layout — when to reach for it.",
      "examples": [
        "Slide title aligned left over a single content area — flexible workhorse layout.",
        "Title plus a single chart on the left and supporting copy on the right."
      ]
    },
    "description": {
      "type": "string",
      "description": "Longer prose describing the layout structure and ideal use cases.",
      "examples": [
        "Two equal-width columns under a left-aligned slide title. Use when comparing two ideas, before/after pairs, or paired text + image content."
      ]
    },
    "contentType": {
      "type": "string",
      "enum": [
        "Title",
        "Text",
        "List",
        "Image",
        "Number",
        "Metric",
        "Chart",
        "Table",
        "Code",
        "Video",
        "Quote",
        "Timeline"
      ],
      "description": "Primary kind of content the layout holds. Drives pickers and AI placement decisions. Metric is the canonical numeric/KPI category; Number remains an accepted legacy label."
    },
    "contentMultiple": {
      "type": "string",
      "enum": [
        "None",
        "1x",
        "2x",
        "3x",
        "4x",
        "5x",
        "6x"
      ],
      "description": "How many parallel content blocks the layout exposes ('2x' = two-column, '3x' = three-up, etc.)."
    },
    "contentAlignment": {
      "type": "string",
      "enum": [
        "None",
        "Left",
        "Center"
      ],
      "description": "Default horizontal alignment of the content area."
    },
    "contentBox": {
      "type": "boolean",
      "description": "Whether the content area is rendered inside a visible box / card.",
      "examples": [
        false,
        true
      ]
    },
    "contentTypeChartPrimary": {
      "type": "string",
      "enum": [
        "None",
        "Top",
        "Bottom",
        "Left",
        "Right"
      ],
      "description": "For chart layouts, where the primary chart sits relative to the rest of the content."
    },
    "contentTypeImageFill": {
      "type": "string",
      "enum": [
        "None",
        "Crop",
        "Fit"
      ],
      "description": "For image layouts, how the image fills its slot."
    },
    "contentTypeListBullet": {
      "type": "string",
      "enum": [
        "None",
        "Character",
        "Image"
      ],
      "description": "For list layouts, how bullets are rendered."
    },
    "contentTypeListHeading": {
      "type": "boolean",
      "description": "For list layouts, whether each list item carries a heading.",
      "examples": [
        false,
        true
      ]
    },
    "slideTag": {
      "type": "boolean",
      "description": "Whether the layout includes a small slide-level tag / label region above or near the title.",
      "examples": [
        false,
        true
      ]
    },
    "slideTitle": {
      "type": "boolean",
      "description": "Whether the layout includes a slide title region.",
      "examples": [
        true,
        false
      ]
    },
    "slideSubtitle": {
      "type": "boolean",
      "description": "Whether the layout includes a slide-level subtitle or supporting-description region. When placeholders is present, this is true exactly when the layout exposes a placeholder with type 'subtitle'.",
      "examples": [
        false,
        true
      ]
    },
    "slideTitleAlignment": {
      "type": "string",
      "enum": [
        "None",
        "Left",
        "Center"
      ],
      "description": "Horizontal alignment of the slide title region."
    },
    "slideImage": {
      "type": "boolean",
      "description": "Whether the layout includes a dedicated slide-level image region (separate from any content image).",
      "examples": [
        false,
        true
      ]
    },
    "slideImageAlignment": {
      "type": "string",
      "enum": [
        "None",
        "Top",
        "Bottom",
        "Left",
        "Right",
        "Background"
      ],
      "description": "Where the slide-level image sits relative to the content."
    },
    "slideLayoutDirection": {
      "type": "string",
      "enum": [
        "None",
        "Horizontal",
        "Vertical"
      ],
      "description": "Axis along which the layout's primary regions are arranged."
    },
    "placeholders": {
      "type": "array",
      "items": { "$ref": "#/$defs/Placeholder" },
      "description": "Ordered regions the layout exposes. The engine fills 'title', 'subtitle', and 'tag' placeholders from Slide.title, Slide.subtitle, and Slide.tag. Other placeholders are content-kind hints for renderers and pickers. Slide content itself lives either in root payload fields or promoted region keys on Slide. Chrome — slide number, footer, date — is not included here; it is owned by Design.header / Design.footer."
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Free-form labels for filtering and search.",
      "examples": [
        [
          "title",
          "minimal"
        ],
        [
          "chart",
          "kpi"
        ],
        [
          "two-column",
          "comparison"
        ]
      ]
    },
    "preview": {
      "type": "object",
      "description": "Visual previews of the record, used by picker UIs and inline rendering. All sub-fields are optional; engines fall back gracefully when previews aren't available.",
      "properties": {
        "src": {
          "type": "string",
          "format": "uri",
          "description": "Main preview image (PNG/JPG). Used as the primary visual in picker UIs and previews of the record.",
          "examples": [
            "https://www.pptx.gallery/layouts/title-left.png"
          ]
        },
        "thumbnailSrc": {
          "type": "string",
          "format": "uri",
          "description": "Smaller thumbnail preview suited to dense grid views.",
          "examples": [
            "https://www.pptx.gallery/layouts/title-left.thumbnail.png"
          ]
        },
        "vectorSrc": {
          "type": "string",
          "format": "uri",
          "description": "SVG / vector preview for crisp scaling at any size.",
          "examples": [
            "https://www.pptx.gallery/layouts/title-left.svg"
          ]
        }
      }
    },
    "composition": {
      "$ref": "#/$defs/Composition"
    }
  },
  "$defs": {
    "Placeholder": {
      "type": "object",
      "required": [
        "type"
      ],
      "description": "A single region inside a slide layout. Title, subtitle, and tag placeholders bind to the corresponding Slide fields; other placeholders describe the intended content kind for that region. The array order in the surrounding 'placeholders' field preserves layout region order.",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "title",
            "subtitle",
            "tag",
            "text",
            "metric",
            "quote",
            "timeline",
            "list",
            "chart",
            "picture",
            "table",
            "media",
            "diagram",
            "code"
          ],
          "description": "OPF placeholder kind. 'text' and 'list' are flexible textual content regions. 'metric' is a numeric/KPI content region filled by a metric payload, including its optional label, description, unit, delta, and trend. The named kinds describe a specific content role used by pickers, AI generation, and engine defaulting. 'picture' and legacy 'diagram' use image payloads; 'media' uses video. Quote and timeline use their corresponding payload fields."
        }
      }
    },
    "Composition": {
      "type": "object",
      "additionalProperties": false,
      "description": "Portable dynamic composition. Slide fields override the resolved layout. Nested groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions.",
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "auto",
            "grid",
            "row",
            "column"
          ],
          "description": "auto chooses a grid from available space and content; grid uses columns; row and column use one horizontal or vertical track."
        },
        "columns": {
          "type": "integer",
          "minimum": 1,
          "maximum": 12,
          "description": "Column count for grid. In auto mode this caps the number of columns."
        },
        "gap": {
          "type": "number",
          "minimum": 0,
          "maximum": 0.1,
          "description": "Space between cells as a fraction of the container short edge (canvas at slide root). Default 0.03333333333333333."
        },
        "padding": {
          "type": "number",
          "minimum": 0,
          "maximum": 0.2,
          "description": "Inset as a fraction of the container short edge. Default 0.08 on a slide, 0 inside a group."
        },
        "weights": {
          "type": "array",
          "minItems": 1,
          "maxItems": 12,
          "items": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 100
          },
          "description": "Relative track sizes: columns for row/grid/auto, rows for column. Omitted tracks have weight 1; extra weights are ignored."
        },
        "minFontSize": {
          "type": "number",
          "minimum": 8,
          "maximum": 32,
          "description": "Minimum readable text size in reference pixels at a 720-pixel canvas short edge. Default 16. Overflow is diagnosed when text cannot fit at this size."
        },
        "overflow": {
          "type": "string",
          "enum": [
            "warn",
            "error"
          ],
          "description": "warn returns diagnostics for content that does not fit; error rejects layout. Content is never silently removed. Default warn."
        }
      }
    }
  }
}
