{
  "name": "Patter",
  "description": "Browser-based step sequencer and beat pattern synthesizer. Runs entirely client-side — there is no server API. Patter is local-only by design and deliberately does not embed an LLM; this file exists so an external agent can drive the UI directly or generate a mix file offline and load it via the import-state action.",
  "stateSchemaUrl": "https://patter-web-app.uc.r.appspot.com/schema/patter-mix.v1.schema.json",
  "recommendedFlow": "For bulk or precise changes, generate a JSON file matching stateSchemaUrl and drive the import-state action rather than clicking every beat cell individually. Use the DOM actions below for incremental edits or for verifying the result of an import.",
  "actions": [
    {
      "id": "toggle-beat",
      "description": "Toggles a single beat cell on/off within a track.",
      "selector": "[data-action=\"toggle-beat\"]",
      "identifiers": [
        "Nearest ancestor's data-track-id attribute identifies the track.",
        "The element's own data-beat-index attribute (0-based) identifies the beat."
      ]
    },
    {
      "id": "play-stop",
      "description": "Starts or stops playback of the active mix. Toggles between the two states.",
      "selector": "[data-action=\"play-stop\"]",
      "identifiers": []
    },
    {
      "id": "add-track",
      "description": "Adds a new (empty, default-length) track to the active mix.",
      "selector": "[data-action=\"add-track\"]",
      "identifiers": []
    },
    {
      "id": "delete-track",
      "description": "Deletes a track from the active mix.",
      "selector": "[data-action=\"delete-track\"]",
      "identifiers": ["Nearest ancestor's data-track-id attribute identifies the track."]
    },
    {
      "id": "set-bpm",
      "description": "Sets the active mix's global tempo in beats per minute.",
      "selector": "[data-action=\"set-bpm\"]",
      "identifiers": ["Numeric input — set its value directly."]
    },
    {
      "id": "set-track-length",
      "description": "Sets a track's length in beats.",
      "selector": "[data-action=\"set-track-length\"]",
      "identifiers": [
        "Nearest ancestor's data-track-id attribute identifies the track.",
        "Numeric input — set its value directly."
      ]
    },
    {
      "id": "set-track-bpm",
      "description": "Sets or clears a track's per-track BPM override (empty value inherits the mix's global bpm).",
      "selector": "[data-action=\"set-track-bpm\"]",
      "identifiers": [
        "Nearest ancestor's data-track-id attribute identifies the track.",
        "Numeric input — set its value directly."
      ]
    },
    {
      "id": "set-track-delay",
      "description": "Sets or clears a track's per-beat timing offset in milliseconds (negative is earlier, positive is later).",
      "selector": "[data-action=\"set-track-delay\"]",
      "identifiers": [
        "Nearest ancestor's data-track-id attribute identifies the track.",
        "Numeric input — set its value directly."
      ]
    },
    {
      "id": "toggle-mute",
      "description": "Mutes or unmutes a track.",
      "selector": "[data-action=\"toggle-mute\"]",
      "identifiers": ["Nearest ancestor's data-track-id attribute identifies the track."]
    },
    {
      "id": "select-mix",
      "description": "Switches the active mix.",
      "selector": "[data-action=\"select-mix\"]",
      "identifiers": ["A <select> element — set its value to the target mix's id."]
    },
    {
      "id": "new-mix",
      "description": "Creates a new, empty mix and makes it active.",
      "selector": "[data-action=\"new-mix\"]",
      "identifiers": []
    },
    {
      "id": "duplicate-mix",
      "description": "Duplicates the active mix.",
      "selector": "[data-action=\"duplicate-mix\"]",
      "identifiers": []
    },
    {
      "id": "delete-mix",
      "description": "Deletes the active mix.",
      "selector": "[data-action=\"delete-mix\"]",
      "identifiers": []
    },
    {
      "id": "import-state",
      "description": "Opens a native file picker; select a JSON file matching stateSchemaUrl to replace all mixes currently stored in this browser.",
      "selector": "[data-action=\"import-state\"]",
      "identifiers": []
    },
    {
      "id": "export-state",
      "description": "Downloads the current app state (all mixes) as a JSON file matching stateSchemaUrl.",
      "selector": "[data-action=\"export-state\"]",
      "identifiers": []
    },
    {
      "id": "download-mix",
      "description": "Renders and downloads the active mix as an audio file (WAV/MP3/AAC, whichever format is currently selected). This is rendered audio, not the JSON state format described by stateSchemaUrl.",
      "selector": "[data-action=\"download-mix\"]",
      "identifiers": []
    }
  ]
}
