{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openmods.net/manifest.schema.json",
  "title": "OpenMods Manifest",
  "description": "Schema for an openmods.json file. One manifest = one mod. Multi-mod repositories use multiple manifests (each named openmods.json or openmods-*.json) at different paths, each declaring its own `slug`.",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Optional pointer to this schema for IDE autocomplete and validation. Ignored by OpenMods at sync time."
    },
    "schemaVersion": {
      "type": "integer",
      "const": 2,
      "description": "Manifest schema version. Always 2 for the current OpenMods format."
    },
    "slug": {
      "type": "string",
      "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
      "maxLength": 140,
      "description": "Stable per-repo identifier for this mod. Required when a repository contains more than one openmods.json. Kebab-case (lowercase letters, digits, single hyphens). DO NOT CHANGE AFTER PUBLISHING — it's the row key that lets OpenMods recognise this mod across syncs."
    },
    "name": {
      "type": "string",
      "description": "First-publish display name. Only honoured when OpenMods creates the row from a freshly-discovered manifest; later edits in the dashboard win on subsequent syncs."
    },
    "supportedGameId": {
      "oneOf": [
        { "type": "integer", "minimum": 1 },
        { "type": "string", "minLength": 1 }
      ],
      "description": "Which game this mod targets. Accepts either the numeric Game.Id (e.g. 1) or the game's slug string (e.g. \"subnautica\"). Required when a repository contains more than one openmods.json.",
      "examples": [1, "subnautica", "below-zero"]
    },
    "releaseAssets": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 },
      "description": "Glob patterns (e.g. \"MyMod.SN-*.zip\") that filter which GitHub release assets sync into this mod's releases. Empty or unset = all assets match. A release with zero matching assets produces no Release row for this mod.",
      "examples": [["AD3D_LightSolution.SN.zip"], ["*.zip", "!*-source.zip"]]
    },
    "primaryAsset": {
      "type": "string",
      "description": "Glob picking the primary downloadable asset — the file the Download button points to. Falls back to the first matching releaseAssets entry if unset or no match.",
      "examples": ["AD3D_LightSolution.SN.zip"]
    },
    "readme": {
      "type": "string",
      "description": "Path to a custom markdown file used as this mod's page body. Manifest-directory-relative by default (\"README.md\" → next to this openmods.json); a leading \"/\" makes it repo-root-relative. Falls back to the repository's default README if unset or missing.",
      "examples": ["README.md", "docs/details.md", "/README.md"]
    },
    "thumbnail": {
      "type": "string",
      "description": "Thumbnail image for the mod card and detail page. Either a filename inside the media/ folder next to this manifest (e.g. \"Screenshot_10.png\") or an absolute URL.",
      "examples": ["Screenshot_10.png", "https://raw.githubusercontent.com/user/repo/main/cover.png"]
    },
    "media": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "url": {
            "type": "string",
            "description": "Filename inside the media/ folder next to this manifest, or an absolute URL (including YouTube/Vimeo embeds)."
          },
          "type": {
            "type": "string",
            "enum": ["image", "video"],
            "description": "Media kind. If omitted, inferred from the URL (file extension or known video host)."
          },
          "label": {
            "type": "string",
            "description": "Optional alt/caption text. Currently stored but not rendered."
          }
        },
        "required": ["url"]
      },
      "description": "Gallery shown on the mod page. Order in the array = display order."
    },
    "links": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "label": { "type": "string", "description": "Visible link text." },
          "url": { "type": "string", "description": "Link target (absolute URL)." },
          "icon": {
            "type": "string",
            "description": "Material Symbols icon name, or any single emoji.",
            "examples": ["code", "link", "🔗"]
          }
        },
        "required": ["label", "url"]
      },
      "description": "External links shown in the mod page sidebar (Discord, wiki, source, etc.)."
    },
    "faq": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "question": { "type": "string" },
          "answer": { "type": "string" }
        },
        "required": ["question", "answer"]
      },
      "description": "FAQ accordion shown on the mod page. Empty entries are dropped."
    },
    "install": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "path": {
          "type": "string",
          "description": "Game-root-relative path the archive contents land under. Empty = archive contents extract to the game root.",
          "examples": ["BepInEx/plugins", "Mods"]
        },
        "custom-path": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Per-file / per-folder overrides. Keys are paths inside the archive, values are paths relative to the game root. Anything not listed falls back to `path` (or the game root). Stored on the mod and surfaced for installers — not interpreted by OpenMods itself."
        }
      },
      "description": "Install instructions consumed by OpenModsManager and similar installers."
    },
    "add-on-of": {
      "oneOf": [
        { "type": "integer", "minimum": 1 },
        { "type": "null" }
      ],
      "description": "Numeric ID of the parent mod this is an add-on of. Use `null` to explicitly clear the relationship; omit the field to leave the dashboard value untouched."
    },
    "dependencies": {
      "type": "object",
      "description": "Per-release dependency map. Top-level key = a release tag of THIS mod (e.g. \"v2.0.3\"). Value = the list of mods required for that release. Releases not listed inherit nothing — dashboard edits on older versions survive subsequent syncs.",
      "additionalProperties": {
        "type": "array",
        "items": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "modId": {
              "type": "integer",
              "minimum": 1,
              "description": "Numeric ID of the required mod (find it in the mod's URL on openmods.net)."
            },
            "release": {
              "type": "string",
              "description": "Exact version pin. Overrides fromRelease/toRelease when set. Leave all three empty to require \"any version of this mod\"."
            },
            "fromRelease": {
              "type": "string",
              "description": "Minimum required version of the dependency (inclusive). Open lower bound if omitted."
            },
            "toRelease": {
              "type": "string",
              "description": "Maximum required version of the dependency (inclusive). Open upper bound if omitted."
            }
          },
          "required": ["modId"]
        }
      },
      "examples": [
        {
          "v2.0.3": [
            { "modId": 31, "fromRelease": "v6.0.0-pre.1" },
            { "modId": 30 }
          ]
        }
      ]
    }
  },
  "examples": [
    {
      "$schema": "https://openmods.net/manifest.schema.json",
      "schemaVersion": 2,
      "slug": "ad3d-light-solution-sn",
      "name": "AD3D Light Solution (Subnautica)",
      "supportedGameId": "subnautica",
      "releaseAssets": ["AD3D_LightSolution.SN.zip"],
      "primaryAsset": "AD3D_LightSolution.SN.zip",
      "readme": "README.md",
      "thumbnail": "Screenshot_10.png",
      "media": [
        { "type": "video", "url": "https://youtube.com/shorts/4vQk2nVvaQU" },
        { "type": "image", "url": "Screenshot_10.png" }
      ],
      "install": { "path": "BepInEx/plugins" },
      "add-on-of": 26,
      "dependencies": {
        "v2.0.3": [
          { "modId": 31, "fromRelease": "v6.0.0-pre.1" },
          { "modId": 30 }
        ]
      }
    }
  ]
}
