> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hayami.moe/llms.txt
> Use this file to discover all available pages before exploring further.

# KomentoScript (For script-makers)

> KomentoScript combines custom websites with automated third-party URL sync, allowing you to put Hayami's comments embedder wherever your wish.

<Note>
  The current version of KomentoScript is **v1**.
</Note>

<Warning>
  KomentoScript is in early-phase undergoing testing & can be subject to change. Documentation will be more limited during initial development & testing periods.
</Warning>

KomentoScript combines the logic behind custom websites, as well as allow additional more complex logic, pertaining to additional details such as dates, fallbacks, amongst other things. The syntax is inspired by similar syntax used in MAL-Sync's ChibiScript, except more catered more towards rendering Hayami on sites itself and use of Hayami's different rendering modes.

Before getting started, <Icon icon="code-fork" /> fork [Hayami's KomentoScript Pages Builder](https://github.com/HayamiKomento/komentoscript-pages) if you want to host your own multi-site endpoint. When ready, read the “Quick start guide” to understand KomentoScript's details, or find the schema attached. Hayami also accepts manually importing KomentoScript files instead of via a URL to test functionality.

Primary schema: [komentoscript-pack.schema.json](https://github.com/HayamiKomento/komentoscript-pages/blob/master/scripts/validation/schema/komentoscript-pack.schema.json) ([raw](https://raw.githubusercontent.com/HayamiKomento/komentoscript-pages/master/scripts/validation/schema/komentoscript-pack.schema.json)).

***

## Quick Start

KomentoScript organises each site into a pack. A pack is one JSON object with metadata and a list of targets. An example valid KomentoScript is below.

```json theme={null}
{
  "komentoVersion": "1.0",
  "id": "hayami-example",
  "targets": [
    {
      "targetId": "example.watch",
      "match": {
        "origins": ["https://example.com"],
        "pathGlobs": ["/watch/*"]
      },
      "extract": {
        "animeTitle": {
          "selector": "h1.title",
          "attr": "text",
          "required": true
        },
        "episodeNumber": {
          "pipeline": [
            ["querySelector", ".episode"],
            ["text"],
            ["regex", "(\\d+)"],
            ["number"]
          ],
          "required": true
        }
      },
      "placement": {
        "display": "below",
        "mountSelector": "main",
        "commentsBackgroundColor": "#1b1f27" // optional
      }
    }
  ]
}
```

This pack adds a single target for `https://example.com/watch/*`, extracts the anime title and episode number, and mounts the UI below the main content.

### Required fields

* Top-level: `komentoVersion`, `id`, `targets[]`.
* Each target: `targetId`, `match.origins[]`, and `extract.animeTitle` + `extract.episodeNumber`.

If these are missing or invalid, the pack is rejected.

***

## Schema-first field reference

Top-level:

* `komentoVersion: string`
* `id: string`
* `name?: string`
* `updatedAt?: string`
* `appliesTo?: string[]`
* `tags?: string[]`
* `profiles?: Record<string, { enabledTargets: string[] }>`
* `targets: Target[]`

Target:

* `targetId: string`
* `priority?: number`
* `extends?: string`
* `mergeMode?: "replace" | "deep"`
* `match: Match`
* `extract: Extract`
* `placement?: PlacementSingle | PlacementMap`
* `mapping?: object`

Match:

* `origins: string[]` (required, non-empty)
* `pathGlobs?: string[]`
* `excludePathGlobs?: string[]`

Extract fields:

* `animeTitle` (required)
* `episodeNumber` (required)
* `episodeReleaseDate`
* `anilistId`
* `malId`

Each extract field can use selector mode or pipeline mode.

### Matching pages

```json theme={null}
"match": {
  "origins": ["https://example.com"],
  "pathGlobs": ["/watch/*"],
  "excludePathGlobs": ["/watch/trailer/*"]
}
```

* `origins` must exactly equal `location.origin` (e.g. `https://example.com`).
* `pathGlobs` and `excludePathGlobs` use `*` as a wildcard on the path.

### Extracting data

Supported extract variables:

* `animeTitle` (required)
* `episodeNumber` (required)
* `episodeReleaseDate`
* `anilistId`
* `malId`

You can use either simple selectors or pipelines to grab this data.

**Selector** mode:

```json theme={null}
"animeTitle": {
  "selector": "h1.title",
  "xPath": "", // optional
  "attr": "text",
  "required": true
}
```

This works as follows:

* `selector` is a standard **CSS selector string**, passed to document.querySelector.
* `xPath` is used if selector is unavailable.
* `attr` supports `text`, `html`, or any attribute name.

**Pipeline** mode:

```json theme={null}
"episodeNumber": {
  "pipeline": [
    ["querySelector", ".episode"],
    ["text"],
    ["regex", "(\\d+)"],
    ["number"]
  ],
  "required": true
}
```

Supported pipeline steps: `querySelector`, `text`, `trim`, `regex`, `number` .

Pipeline mode uses a workflow-like approach. For the example above, this'd undergo the following:

1. Find an element on the page that matches .episode
2. Read that element’s text content
3. Apply regex (\d+) to that text and extract the first number capture
4. Convert the extracted value to a numeric string (sanitizes to digits/decimal, parses number, then stores as string)

### Placement

```json theme={null}
"placement": {
  "display": "below",
  "mountSelector": "main",
  "anchorSelector": "", // optional, depends on mode
  "mountXPath": "", // optional
  "anchorXPath": "", // optional
  "sidePadding": 240, // optional
  "iconDisplayKind": "text", // optional: "text" | "icon"
  "iconDisplayAction": "popup", // optional: "popup" | "replace"
  "iconDisplayText": "Hayami", // optional
  "commentsBackgroundColor": "#1b1f27" // optional
}
```

* `display`: `"below"`, `"insert"`, `"replace"`, `"popup"`, or `"icon"`.
* `mountSelector` / `anchorSelector` choose where the UI is attached.
* `sidePadding` controls horizontal padding applied to the mounted container.

If using `icon` mode (which is icon/text trigger):

* `iconDisplayKind` controls whether icon mode uses text-based or icon-based trigger styles.
* `iconDisplayAction` controls whether icon click opens popup or performs replace behavior.
* `iconDisplayText` controls label text when text-based icon mode is used.

If `placement` is omitted, runtime falls back to popup mode.

You can also define multiple placements, like so:

```json theme={null}
"placement": {
  "below": {
    "default": true,
    "anchorSelector": "main",
    "mountSelector": "main"
  },
  "popup": {}
}
```

Doing this will, in the above example, set below as default, and allow KomentoScript users to choose how they'd like it mounted on-site, configured by your KomentoScript.

* For popup mode, anchorSelector is mostly irrelevant.
* If anchorSelector is missing, runtime falls back to mountSelector, then body.
* There is also anchorXPath if CSS selectors are unreliable.

***

## Profiles

Profiles let one pack expose multiple target sets and choose which targets are active.

Use cases:

* Keep a stable default while shipping an experimental target in the same pack.
* Switch between target groups for site variants without publishing separate packs.

Schema shape:

```json theme={null}
"profiles": {
  "default": { "enabledTargets": ["example.watch.stable"] },
  "experimental": { "enabledTargets": ["example.watch.stable", "example.watch.new-layout"] }
}
```

Behavior:

* If an active profile id is selected, its `enabledTargets` list is used.
* Otherwise, runtime falls back to `profiles.default`.
* If no valid profile is present (or `enabledTargets` is empty), profile filtering is skipped.
* User per-source target toggles are applied in addition to profile filtering.

***

## More examples

Standalone JSON example files:

* [Minimal pack](https://github.com/HayamiKomento/komentoscript-pages/blob/master/examples/komentoscript-examples/minimal.pack.json)
* [Icon (text) popup](https://github.com/HayamiKomento/komentoscript-pages/blob/master/examples/komentoscript-examples/icon-text-popup.pack.json)
* [Icon (icon) replace](https://github.com/HayamiKomento/komentoscript-pages/blob/master/examples/komentoscript-examples/icon-icon-replace.pack.json)
* [Placement map](https://github.com/HayamiKomento/komentoscript-pages/blob/master/examples/komentoscript-examples/placement-map.pack.json)
* [Profiles pack](https://github.com/HayamiKomento/komentoscript-pages/blob/master/examples/komentoscript-examples/profiles.pack.json)

### Icon mode with text trigger

```json theme={null}
{
  "komentoVersion": "1.0",
  "id": "example-icon-text",
  "targets": [
    {
      "targetId": "example.icon.text",
      "match": {
        "origins": ["https://example.com"],
        "pathGlobs": ["/watch/*"]
      },
      "extract": {
        "animeTitle": { "selector": "h1.title", "attr": "text" },
        "episodeNumber": { "selector": ".episode", "attr": "text" }
      },
      "placement": {
        "display": "icon",
        "mountSelector": "#player-controls",
        "iconDisplayKind": "text",
        "iconDisplayAction": "popup",
        "iconDisplayText": "Open Hayami"
      }
    }
  ]
}
```

### Icon mode with icon trigger + replace action

```json theme={null}
{
  "komentoVersion": "1.0",
  "id": "example-icon-replace",
  "targets": [
    {
      "targetId": "example.icon.replace",
      "match": {
        "origins": ["https://example.com"],
        "pathGlobs": ["/episode/*"]
      },
      "extract": {
        "animeTitle": { "selector": "h1", "attr": "text" },
        "episodeNumber": {
          "pipeline": [["querySelector", ".ep"], ["text"], ["regex", "(\\d+)"], ["number"]]
        }
      },
      "placement": {
        "display": "icon",
        "anchorSelector": "#comments-slot",
        "mountSelector": "#comments-slot",
        "iconDisplayKind": "icon",
        "iconDisplayAction": "replace"
      }
    }
  ]
}
```

### Placement map for user-selectable display modes

```json theme={null}
{
  "placement": {
    "below": {
      "default": true,
      "mountSelector": "main",
      "anchorSelector": "main",
      "sidePadding": 24
    },
    "icon": {
      "mountSelector": "#player-controls",
      "iconDisplayKind": "text",
      "iconDisplayAction": "popup",
      "iconDisplayText": "Discuss"
    },
    "popup": {}
  }
}
```

***

## Multi-target packs

You can include multiple targets in a single pack.

```json theme={null}
{
  "komentoVersion": "1.0",
  "id": "multi-site-pack",
  "targets": [
    {
      "targetId": "example.watch",
      "match": { "origins": ["https://example.com"], "pathGlobs": ["/watch/*"] },
      "extract": { /* ... */ },
      "placement": { "display": "below", "mountSelector": "main" }
    },
    {
      "targetId": "othersite.view",
      "match": { "origins": ["https://othersite.com"], "pathGlobs": ["/view/*"] },
      "extract": { /* ... */ },
      "placement": { "display": "insert", "mountSelector": "#player" }
    }
  ]
}
```

***

## Getting started with KomentoScript Pages Builder

To get started, <Icon icon="code-fork" />  [fork the repository](https://deploy.workers.cloudflare.com/?url=https://github.com/HayamiKomento/komentoscript-pages) from GitHub. You can also one-click the Cloudflare Pages, or Netlify deployment buttons, which'll do this & auto-deploy for you. For GitHub pages, fork the repository, and enable GitHub Pages through the repository settings, and re-run the deployment workflow through Actions.

Under sites/, this contains JSON files for each site. There are sample files for Crunchyroll and HIDIVE (these are sample files unintended for production-use) that demonstrate the use of KomentoScript. Feel free to rename these files, and use the schema plus this documentation to aid scripting. To test functionality through the Hayami extension, you can import files directly via:\
Hayami settings > KomentoScript > Import file

You can also *validate* that your files fit the schema by `npm run build` . If there are any errors, such as trying to put custom website configurations into KomentoScript without considering the syntax:

```ssh-config theme={null}
PS C:\Users\User\Documents\komentoscript-pages> npm run build

> komentoscript-pages@1.0.0 build
> node ./scripts/build.mjs

Validation failed:
- sites\animeWebsite.json: root must have required property 'komentoVersion'
- sites\animeWebsite.json: root must have required property 'id'
- sites\animeWebsite.json: root must have required property 'targets'
- sites\animeWebsite.json: root unsupported field 'format'.
- sites\animeWebsite.json: root unsupported field 'version'.
- sites\animeWebsite.json: root unsupported field 'exportedAt'.    
- sites\animeWebsite.json: root unsupported field 'mapping'.       
```

Hayami's KomentoScript Pages Builder automatically chains all sites you've configured to a `/all` endpoint when you deploy KomentoScripts Page Builder. You can also individually go to the JSON of each site by going to `/id` (where `id` is your pack ID).
