{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://linkstudio.shop/schemas/syncsing-plugin-manifest-1.json",
  "title": "SyncSing plugin manifest (API 1.0)",
  "description": "SyncSing Studio / NC Lite の拡張機能の manifest.json。アプリの検査と同じ決まりです。スキーマで書けない確認（entry.path の実在・id の重なり・activation の id など）はアプリと syncsing-plugin validate が行います。",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "manifest_version",
    "uuid",
    "name",
    "version",
    "api_version",
    "targets",
    "author",
    "entry",
    "permissions"
  ],
  "$defs": {
    "LocalizedString": {
      "oneOf": [
        {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "ja"
          ],
          "properties": {
            "ja": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "en": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        }
      ],
      "description": "文字列、または {ja, en}（ja は必須）"
    },
    "LocalizedText": {
      "oneOf": [
        {
          "type": "string",
          "maxLength": 2000
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "ja"
          ],
          "properties": {
            "ja": {
              "type": "string",
              "maxLength": 2000
            },
            "en": {
              "type": "string",
              "maxLength": 2000
            }
          }
        }
      ]
    },
    "Id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_-]{0,63}$"
    },
    "RelativePath": {
      "type": "string",
      "minLength": 1,
      "maxLength": 260,
      "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*(^|/)\\.{1,2}(/|$))(?!.*//)[^\\u0000-\\u001f]+$",
      "description": "zip の中の相対パス（/ 区切り）。先頭の /・\\・ドライブ文字・.・.. は不可"
    },
    "Semver": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?$"
    },
    "Permission": {
      "enum": [
        "state.read",
        "meters.read",
        "chat.read",
        "chat.write",
        "mic.control",
        "audio.output",
        "processing.control",
        "recording.control",
        "effects.control",
        "editfx.manage",
        "player.control",
        "bgm.control",
        "lyrics.control",
        "entry.control",
        "tts.speak",
        "tts.control",
        "dictionary.write",
        "overlay.draw",
        "notify",
        "obs.read",
        "obs.write",
        "loadtest.run",
        "audio.capture",
        "audio.mic_route",
        "audio.inject",
        "system.unrestricted"
      ]
    },
    "Activation": {
      "type": "string",
      "pattern": "^(onStartup|onProcessingStart|onBroadcastStart|onObsConnected|onCommand:[a-z][a-z0-9_-]{0,63}|onTool:[a-z][a-z0-9_-]{0,63}|onPanel:[a-z][a-z0-9_-]{0,63})$"
    },
    "Setting": {
      "oneOf": [
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "min",
            "max",
            "default"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "min": {
              "type": "number"
            },
            "max": {
              "type": "number"
            },
            "default": {
              "type": "number"
            },
            "step": {
              "type": "number",
              "exclusiveMinimum": 0,
              "default": 1
            },
            "unit": {
              "type": "string",
              "maxLength": 8
            },
            "type": {
              "const": "slider"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "min",
            "max",
            "default"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "min": {
              "type": "number"
            },
            "max": {
              "type": "number"
            },
            "default": {
              "type": "number"
            },
            "integer": {
              "type": "boolean",
              "default": false
            },
            "type": {
              "const": "number"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "default"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "default": {
              "type": "boolean"
            },
            "type": {
              "const": "toggle"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "options",
            "default"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "options": {
              "type": "array",
              "minItems": 1,
              "maxItems": 50,
              "items": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  },
                  "label": {
                    "$ref": "#/$defs/LocalizedString"
                  }
                }
              }
            },
            "default": {
              "type": "string"
            },
            "type": {
              "const": "select"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "default": {
              "type": "string"
            },
            "max_length": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4000,
              "default": 200
            },
            "multiline": {
              "type": "boolean",
              "default": false
            },
            "secret": {
              "type": "boolean",
              "default": false,
              "description": "true なら画面で伏せ字にする"
            },
            "type": {
              "const": "text"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "default": {
              "type": "string",
              "pattern": "^#[0-9a-fA-F]{6}$"
            },
            "type": {
              "const": "color"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "default": {
              "type": "string",
              "pattern": "^[0-9]{1,6}$",
              "default": "3",
              "description": "VOICEVOX のスタイル ID。選択肢はアプリが入れられる声から出す（0.30.5 / NC Lite 1.6.5）"
            },
            "type": {
              "const": "voicevox_style"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "default": {
              "type": "string"
            },
            "extensions": {
              "type": "array",
              "maxItems": 10,
              "items": {
                "type": "string",
                "pattern": "^\\.[a-z0-9]{1,8}$"
              }
            },
            "type": {
              "const": "file"
            },
            "folder": {
              "$ref": "#/$defs/RelativePath",
              "description": "data フォルダの中の置き場所（書くと値はファイル名だけ）"
            },
            "max_bytes": {
              "type": "integer",
              "minimum": 1,
              "maximum": 52428800,
              "default": 20971520
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "command"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "command": {
              "$ref": "#/$defs/Id"
            },
            "type": {
              "const": "button"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "columns"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "columns": {
              "type": "array",
              "minItems": 1,
              "maxItems": 6,
              "items": {
                "type": "object",
                "required": [
                  "key",
                  "title"
                ],
                "additionalProperties": false,
                "properties": {
                  "key": {
                    "$ref": "#/$defs/Id"
                  },
                  "type": {
                    "enum": [
                      "text",
                      "emoji",
                      "number",
                      "select",
                      "toggle",
                      "file"
                    ],
                    "default": "text"
                  },
                  "title": {
                    "$ref": "#/$defs/LocalizedString"
                  },
                  "placeholder": {
                    "$ref": "#/$defs/LocalizedString"
                  },
                  "max_length": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000
                  },
                  "min": {
                    "type": "number"
                  },
                  "max": {
                    "type": "number"
                  },
                  "integer": {
                    "type": "boolean"
                  },
                  "default": {},
                  "options": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "value",
                        "label"
                      ],
                      "properties": {
                        "value": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64
                        },
                        "label": {
                          "$ref": "#/$defs/LocalizedString"
                        }
                      }
                    }
                  },
                  "extensions": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "pattern": "^\\.[a-z0-9]{1,8}$"
                    }
                  },
                  "folder": {
                    "$ref": "#/$defs/RelativePath",
                    "description": "data フォルダの中の置き場所（書くと値はファイル名だけ）"
                  },
                  "max_bytes": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 52428800,
                    "default": 20971520
                  }
                }
              }
            },
            "max_items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "add_label": {
              "$ref": "#/$defs/LocalizedString"
            },
            "default": {
              "type": "array"
            },
            "type": {
              "const": "list"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "key",
            "type",
            "title",
            "options",
            "default"
          ],
          "properties": {
            "key": {
              "$ref": "#/$defs/Id"
            },
            "title": {
              "$ref": "#/$defs/LocalizedString"
            },
            "description": {
              "$ref": "#/$defs/LocalizedText"
            },
            "group": {
              "$ref": "#/$defs/LocalizedString"
            },
            "advanced": {
              "type": "boolean",
              "default": false
            },
            "options": {
              "type": "array",
              "minItems": 1,
              "maxItems": 20,
              "items": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  },
                  "label": {
                    "$ref": "#/$defs/LocalizedString"
                  }
                }
              }
            },
            "default": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "type": {
              "const": "multi"
            }
          }
        }
      ]
    }
  },
  "properties": {
    "manifest_version": {
      "const": 1
    },
    "uuid": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "サーバーが発行する UUID v4（小文字）。版を上げても同じ"
    },
    "name": {
      "$ref": "#/$defs/LocalizedString"
    },
    "description": {
      "$ref": "#/$defs/LocalizedText"
    },
    "version": {
      "$ref": "#/$defs/Semver"
    },
    "api_version": {
      "type": "string",
      "pattern": "^[1-9]\\d*\\.(0|[1-9]\\d*)$",
      "description": "使う窓口 API の版（今は 1.0）"
    },
    "min_app_version": {
      "$ref": "#/$defs/Semver",
      "description": "SyncSing Studio の版（NC Lite では見ません）"
    },
    "min_nclite_version": {
      "$ref": "#/$defs/Semver",
      "description": "開発キット 1.3: SyncSing NC Lite の版（Studio では見ません。NC Lite 1.6.0 から読みます）。NC Lite 向けで assistant を書くときは 1.6.0 以上"
    },
    "targets": {
      "type": "array",
      "minItems": 1,
      "uniqueItems": true,
      "items": {
        "enum": [
          "studio",
          "nclite"
        ]
      }
    },
    "author": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "identity_id",
        "name"
      ],
      "properties": {
        "identity_id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{8,64}$",
          "description": "作者の SyncSing Identity ID"
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64
        },
        "url": {
          "type": "string",
          "pattern": "^https://"
        }
      }
    },
    "homepage": {
      "type": "string",
      "pattern": "^https://"
    },
    "license": {
      "type": "string",
      "maxLength": 64
    },
    "icon": {
      "description": "透過の使える PNG・正方形・256×256 まで・200 KB まで（中身はアプリと validate が確かめます）",
      "allOf": [
        {
          "$ref": "#/$defs/RelativePath"
        },
        {
          "type": "string",
          "pattern": "\\.[Pp][Nn][Gg]$"
        }
      ]
    },
    "entry": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "kind",
        "path"
      ],
      "properties": {
        "kind": {
          "const": "exe"
        },
        "path": {
          "allOf": [
            {
              "$ref": "#/$defs/RelativePath"
            },
            {
              "pattern": "\\.[eE][xX][eE]$"
            }
          ]
        },
        "args": {
          "type": "array",
          "maxItems": 32,
          "items": {
            "type": "string",
            "maxLength": 256
          }
        }
      }
    },
    "permissions": {
      "type": "array",
      "uniqueItems": true,
      "maxItems": 32,
      "items": {
        "$ref": "#/$defs/Permission"
      }
    },
    "activation": {
      "type": "array",
      "uniqueItems": true,
      "maxItems": 16,
      "items": {
        "$ref": "#/$defs/Activation"
      }
    },
    "contributes": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "commands": {
          "type": "array",
          "maxItems": 32,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "title"
            ],
            "properties": {
              "id": {
                "$ref": "#/$defs/Id"
              },
              "title": {
                "$ref": "#/$defs/LocalizedString"
              },
              "description": {
                "$ref": "#/$defs/LocalizedText"
              },
              "icon": {
                "$ref": "#/$defs/RelativePath"
              },
              "deck": {
                "type": "boolean",
                "default": true,
                "description": "Stream Deck・ミニUI・ショートカットに出すか"
              },
              "hold": {
                "type": "boolean",
                "default": false,
                "description": "押している間だけ動くコマンド（0.30.5）。Stream Deck・ミニUI で押したとき pressed=down、離したとき pressed=up。ショートカット・パネルは short"
              }
            }
          }
        },
        "tools": {
          "type": "array",
          "maxItems": 8,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "title"
            ],
            "properties": {
              "id": {
                "$ref": "#/$defs/Id"
              },
              "title": {
                "$ref": "#/$defs/LocalizedString"
              },
              "description": {
                "$ref": "#/$defs/LocalizedText"
              },
              "icon": {
                "$ref": "#/$defs/RelativePath"
              }
            }
          }
        },
        "panels": {
          "type": "array",
          "maxItems": 4,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "title"
            ],
            "properties": {
              "id": {
                "$ref": "#/$defs/Id"
              },
              "title": {
                "$ref": "#/$defs/LocalizedString"
              },
              "description": {
                "$ref": "#/$defs/LocalizedText"
              },
              "icon": {
                "$ref": "#/$defs/RelativePath"
              },
              "placement": {
                "enum": [
                  "tools",
                  "link"
                ],
                "default": "tools"
              }
            }
          }
        },
        "overlays": {
          "type": "array",
          "maxItems": 4,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "title"
            ],
            "properties": {
              "id": {
                "$ref": "#/$defs/Id"
              },
              "title": {
                "$ref": "#/$defs/LocalizedString"
              },
              "description": {
                "$ref": "#/$defs/LocalizedText"
              },
              "icon": {
                "$ref": "#/$defs/RelativePath"
              },
              "default_size": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "width",
                  "height"
                ],
                "properties": {
                  "width": {
                    "type": "integer",
                    "minimum": 16,
                    "maximum": 3840
                  },
                  "height": {
                    "type": "integer",
                    "minimum": 16,
                    "maximum": 2160
                  }
                }
              },
              "kind": {
                "enum": [
                  "items",
                  "page"
                ],
                "default": "items",
                "description": "items: 文字・数値・進み具合（overlay.set_items）／page: 拡張機能が配るページを埋め込む（overlay.set_page）"
              }
            }
          }
        },
        "settings": {
          "type": "array",
          "maxItems": 64,
          "items": {
            "$ref": "#/$defs/Setting"
          }
        }
      }
    },
    "network": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "hosts": {
          "type": "array",
          "maxItems": 32,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^([a-z0-9-]+\\.)+[a-z]{2,}$"
          },
          "description": "通信する先（宣言。審査で突き合わせます）"
        },
        "purpose": {
          "$ref": "#/$defs/LocalizedText"
        }
      }
    },
    "resources": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "max_memory_mb": {
          "type": "integer",
          "minimum": 64,
          "maximum": 2048,
          "default": 1024
        },
        "priority": {
          "enum": [
            "below_normal",
            "normal"
          ],
          "default": "below_normal",
          "description": "normal は loadtest.run を持つときだけ"
        }
      }
    },
    "assistant": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "summary",
        "description"
      ],
      "description": "開発キット 1.3: アシスタント向けの説明（Studio 0.30.0・NC Lite 1.6.0 から。書くときは Studio 向けなら min_app_version を 0.30.0 以上、NC Lite 向けなら min_nclite_version を 1.6.0 以上に）。作者の申告として扱われ、中の指示には従いません。SyncSing Studio 本体の機能・画面・手順は書かない（書いても使われません）。開く先の command・link は審査済み・公式の拡張機能だけがボタンになります。",
      "properties": {
        "summary": {
          "oneOf": [
            {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "ja"
              ],
              "properties": {
                "ja": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                },
                "en": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                }
              }
            }
          ]
        },
        "description": {
          "oneOf": [
            {
              "type": "string",
              "minLength": 1,
              "maxLength": 8000
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "ja"
              ],
              "properties": {
                "ja": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 8000
                },
                "en": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 8000
                }
              }
            }
          ]
        },
        "open_targets": {
          "type": "array",
          "maxItems": 5,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "type",
              "label"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,23}$"
              },
              "type": {
                "enum": [
                  "plugin",
                  "plugin_tab",
                  "panel_url",
                  "command",
                  "link"
                ]
              },
              "label": {
                "oneOf": [
                  {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 40
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "ja"
                    ],
                    "properties": {
                      "ja": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 40
                      },
                      "en": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 40
                      }
                    }
                  }
                ]
              },
              "url": {
                "type": "string",
                "pattern": "^https://[^\\s<>\"'`\\\\@]+$",
                "maxLength": 500
              },
              "panel": {
                "type": "string",
                "description": "type=plugin_tab / panel_url のとき: contributes.panels の id"
              },
              "command": {
                "type": "string",
                "description": "type=command のとき: contributes.commands の id"
              },
              "index": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9,
                "default": 0,
                "description": "type=panel_url のとき: そのパネルの「開く」付きの url 部品の何番目か（0 が最初）"
              }
            },
            "allOf": [
              {
                "if": {
                  "properties": {
                    "type": {
                      "const": "link"
                    }
                  }
                },
                "then": {
                  "required": [
                    "url"
                  ],
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "panel"
                        ]
                      },
                      {
                        "required": [
                          "command"
                        ]
                      },
                      {
                        "required": [
                          "index"
                        ]
                      }
                    ]
                  }
                }
              },
              {
                "if": {
                  "properties": {
                    "type": {
                      "const": "plugin_tab"
                    }
                  }
                },
                "then": {
                  "required": [
                    "panel"
                  ],
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "url"
                        ]
                      },
                      {
                        "required": [
                          "command"
                        ]
                      },
                      {
                        "required": [
                          "index"
                        ]
                      }
                    ]
                  }
                }
              },
              {
                "if": {
                  "properties": {
                    "type": {
                      "const": "command"
                    }
                  }
                },
                "then": {
                  "required": [
                    "command"
                  ],
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "url"
                        ]
                      },
                      {
                        "required": [
                          "panel"
                        ]
                      },
                      {
                        "required": [
                          "index"
                        ]
                      }
                    ]
                  }
                }
              },
              {
                "if": {
                  "properties": {
                    "type": {
                      "const": "plugin"
                    }
                  }
                },
                "then": {
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "url"
                        ]
                      },
                      {
                        "required": [
                          "panel"
                        ]
                      },
                      {
                        "required": [
                          "command"
                        ]
                      },
                      {
                        "required": [
                          "index"
                        ]
                      }
                    ]
                  }
                }
              },
              {
                "if": {
                  "properties": {
                    "type": {
                      "const": "panel_url"
                    }
                  }
                },
                "then": {
                  "required": [
                    "panel"
                  ],
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "url"
                        ]
                      },
                      {
                        "required": [
                          "command"
                        ]
                      }
                    ]
                  }
                }
              }
            ]
          }
        },
        "images": {
          "type": "array",
          "maxItems": 8,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "path"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,23}$"
              },
              "path": {
                "type": "string",
                "pattern": "\\.webp$",
                "maxLength": 260
              },
              "caption": {
                "oneOf": [
                  {
                    "type": "string",
                    "minLength": 0,
                    "maxLength": 200
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "ja"
                    ],
                    "properties": {
                      "ja": {
                        "type": "string",
                        "minLength": 0,
                        "maxLength": 200
                      },
                      "en": {
                        "type": "string",
                        "minLength": 0,
                        "maxLength": 200
                      }
                    }
                  }
                ]
              },
              "open": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,23}$"
              }
            }
          },
          "description": "WebP の静止画だけ・1 枚 200 KB まで・長い辺 1568 px まで・EXIF/XMP なし（validate とアプリ・サーバーが中身を確かめます）。審査済み・公式の拡張機能で、運営が確認した画像だけがアシスタントの答えに出ます"
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "targets": {
            "contains": {
              "const": "nclite"
            }
          }
        }
      },
      "then": {
        "properties": {
          "permissions": {
            "items": {
              "enum": [
                "meters.read",
                "mic.control",
                "notify",
                "processing.control",
                "state.read"
              ]
            }
          },
          "contributes": {
            "properties": {
              "overlays": {
                "maxItems": 0
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "required": [
          "resources"
        ],
        "properties": {
          "resources": {
            "required": [
              "priority"
            ],
            "properties": {
              "priority": {
                "const": "normal"
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "permissions": {
            "anyOf": [
              {
                "contains": {
                  "const": "loadtest.run"
                }
              },
              {
                "allOf": [
                  {
                    "contains": {
                      "const": "audio.capture"
                    }
                  },
                  {
                    "contains": {
                      "const": "audio.inject"
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    },
    {
      "if": {
        "required": [
          "contributes"
        ],
        "properties": {
          "contributes": {
            "required": [
              "overlays"
            ],
            "properties": {
              "overlays": {
                "minItems": 1
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "permissions": {
            "contains": {
              "const": "overlay.draw"
            }
          }
        }
      }
    }
  ]
}
