![NagiManga — タグ 1 つで、ブログに漫画ビューアー](../img/ogp/nagimanga-ogp.png)

**日本語** | [English](README.en.md)

# NagiManga（漫画ビューアー）

[![NagiManga v0.13.0](https://img.shields.io/badge/NagiManga-v0.13.0-c2410c?style=flat-square&logo=bookstack&logoColor=white)](#デモ)
[![NagiLog v0.13.0](https://img.shields.io/badge/NagiLog-v0.13.0-2563eb?style=flat-square&logo=rss&logoColor=white)](README-LOG.md)
[![Latest release](https://img.shields.io/github/v/release/Lichiphen/NagiSwipe?style=flat-square&logo=github&label=release)](https://github.com/Lichiphen/NagiSwipe/releases/latest)
[![GitHub](https://img.shields.io/badge/GitHub-Lichiphen%2FNagiSwipe-181717?style=flat-square&logo=github)](https://github.com/Lichiphen/NagiSwipe)
[![GitLab](https://img.shields.io/badge/GitLab-lichiphen%2Fnagiswipe-FC6D26?style=flat-square&logo=gitlab&logoColor=white)](https://gitlab.com/lichiphen/nagiswipe)
[![Documentation](https://img.shields.io/badge/docs-nagiswipe.pages.dev-F38020?style=flat-square&logo=cloudflarepages&logoColor=white)](https://nagiswipe.pages.dev/nagimanga.html)
[![PHP 8.1+](https://img.shields.io/badge/PHP-8.1%2B-777BB4?style=flat-square&logo=php&logoColor=white)](#2-つの使い方)
[![License: MIT](https://img.shields.io/badge/License-MIT-eab308?style=flat-square)](../LICENSE)

NagiSeries（[NagiSwipe](../README.md#nagiswipe)・NagiManga・[NagiLog](README-LOG.md)）の漫画ビューアーです。

ブログやサイトに **タグを 1 つ貼るだけ** で、クリックしたときに漫画ビューアーが開きます。作品の管理は、データベースのいらない小さな管理画面（PHP）で行います。

v0.4.0 から、文章・画像・漫画を投稿できる個人用LOG「NagiLog」を同梱しています。
スマホの投稿画面、太字、画像の差し替え、漫画カード、分類、6種類のデザイン、日時から作るURLに対応します。v0.6.0 からは、動画と音声も投稿できます。
使い方、メリット・デメリット、設置と検証結果は [NagiLog の README](README-LOG.md) にまとめています。

[![NagiManga・NagiLog をダウンロード（ZIP）](https://img.shields.io/badge/%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89-NagiManga%E3%83%BBNagiLog%EF%BC%88ZIP%EF%BC%89-c2410c?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyLjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI%2BPHBhdGggZD0iTTEyIDN2MTIiLz48cGF0aCBkPSJtNiAxMCA2IDYgNi02Ii8%2BPHBhdGggZD0iTTQgMjBoMTYiLz48L3N2Zz4%3D&logoColor=white&labelColor=9a3412)](https://lichiphen.com/tool/link/r/NagiManga)
[![ドキュメントサイト](https://img.shields.io/badge/%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%82%B5%E3%82%A4%E3%83%88-nagiswipe.pages.dev-F38020?style=for-the-badge&logo=cloudflarepages&logoColor=white&labelColor=b45309)](https://nagiswipe.pages.dev/nagimanga.html)

<!-- TOC -->
## 目次

- [デモ](#デモ)
- [できること](#できること)
- [2 つの使い方](#2-つの使い方)
- [管理画面（PHP）を使う](#管理画面phpを使う)
  - [1. 設置](#1-設置)
  - [2. セットアップ](#2-セットアップ)
  - [3. 設置したら確認すること](#3-設置したら確認すること)
  - [4. 作品を作って共有する](#4-作品を作って共有する)
  - [てがろぐに載せる](#てがろぐに載せる)
- [機能の説明](#機能の説明)
  - [EPUB から作る](#epub-から作る)
  - [小容量版（スマホ用）](#小容量版スマホ用)
  - [パスワード（限定公開）](#パスワード限定公開)
  - [バックアップ](#バックアップ)
  - [更新（v0.2.0 以降）](#更新v020-以降)
  - [管理画面の言語](#管理画面の言語)
  - [設置用コード](#設置用コード)
  - [個別ページ（共有リンク・v0.3.0 以降）](#個別ページ共有リンクv030-以降)
  - [直リンク防止（上級者向け・v0.3.0 以降）](#直リンク防止上級者向けv030-以降)
  - [アクセス解析（v0.13.0 以降）](#アクセス解析v0130-以降)
  - [プラグイン（ゲストモード）](#プラグインゲストモード)
  - [二段階認証（v0.9.1 以降・任意）](#二段階認証v091-以降任意)
  - [パスキー（v0.10.0 以降・任意）](#パスキーv0100-以降任意)
  - [困ったとき](#困ったとき)
  - [data フォルダを守る](#data-フォルダを守る)
  - [管理画面の IP 制限をさらに固くする（任意）](#管理画面の-ip-制限をさらに固くする任意)
- [PHP なし（FTP だけ）で使う](#php-なしftp-だけで使う)
- [セキュリティの考え方](#セキュリティの考え方)
  - [攻撃テスト](#攻撃テスト)
- [開発](#開発)
- [ライセンス](#ライセンス)
<!-- /TOC -->

## デモ

**https://notebook.lichiphen.com/nagimanga/demo.html**

- いろいろな読み方（右から左・見開き／左から右／縦読みのページ漫画・ウェブトゥーン）を試せます
- パスワード付きの見本（パスワードは `demo`）、CLIP STUDIO PAINT の EPUB から取り込んだ作品もあります
- 「ゲストとして管理画面を見る」から、作者が使う管理画面を閲覧のみで見られます（変更はできません）

**NagiLog のデモ: https://notebook.lichiphen.com/nagimanga/**

- 作者が実際に使っているLOGです。投稿、スマホのメニュー、メールのダイアログ、404（ブラックホールの中）を見られます
- 画面のスクリーンショットと説明は [NagiLog の README](README-LOG.md#画面) にあります

**使うかどうか迷っている方、長く使い続けたい方へ:** メリット・デメリット、使っている技術、データの取り出し方を [docs/overview.md](docs/overview.md) にまとめています（開発者でない方向け）。

## できること

**読む人（ビューアー）**

- 右から左（漫画）／左から右／縦読み（1 ページずつ画面に収める「ページ漫画」と、横幅いっぱいの「ウェブトゥーン」）
- 横長の画面では見開き表示（表紙だけ単独にする／しない）。横長の 1 枚絵は自動で単独ページ
- ページ送り: スワイプ、画面の端のタップ、下のバーの ◀ ▶ ボタン、キーボード、マウスホイール（縦読みはマウスでつかんでスクロールもできます）
- 拡大: ピンチ、ダブルタップ、パソコンでは上のバーの「− 100% ＋」ボタン、`+` `-` `0` キー、Ctrl＋ホイール（マウスを画面の上端か下端に近づけるとバーが出ます）
- しおり（次に開いたときは続きから）、ブラウザの「戻る」で閉じる
- スマホでは小さい画像（小容量版）、パソコンや拡大したときは大きい画像（通常版）を自動で読み分け

**作る人（管理画面）**

- 画像のドラッグ＆ドロップ、並べ替え、共有タグの発行（URL は設置したドメインから自動で作られます）
- CLIP STUDIO PAINT などの **漫画 EPUB からそのまま作品を作る**
- パスワード付きの限定公開（読む人はクリックしたときにパスワードを入力）
- バックアップと復元（ZIP 1 つ）
- ゲスト（閲覧のみ）で管理画面を見せるプラグイン（デモ向け）

データベースも外部のライブラリも使っていません（PHP と JavaScript の標準機能だけ）。

## 2 つの使い方

| | 管理画面（PHP）を使う | PHP なし（FTP だけ） |
|---|---|---|
| 向いている人 | 作品を管理画面で扱いたい人（FTP を使うのは最初の設置だけ） | フォルダと URL が分かる人 |
| 画像の追加 | 管理画面でドラッグ＆ドロップ、または EPUB から | FTP で置く |
| パスワード付き公開 | できる | できない（画像の URL を知っていれば誰でも見られます） |
| 必要なもの | PHP 8.1 以上（GD・fileinfo・mbstring・ZipArchive）、`.htaccess` が効くサーバー（Apache 系）推奨 | 静的なファイルを置けるサーバーなら何でも |

---

## 管理画面（PHP）を使う

### 1. 設置

下の **ダウンロード** ボタンから、最新版の ZIP（`nagimanga-vX.Y.Z.zip`）をダウンロードします。ZIP を展開した中身（または、このリポジトリの `manga/server` フォルダの中身）を、サーバーの好きな場所に `nagimanga` などの名前でアップロードします。

[![NagiManga・NagiLog をダウンロード（ZIP）](https://img.shields.io/badge/%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89-NagiManga%E3%83%BBNagiLog%EF%BC%88ZIP%EF%BC%89-c2410c?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyLjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI%2BPHBhdGggZD0iTTEyIDN2MTIiLz48cGF0aCBkPSJtNiAxMCA2IDYgNi02Ii8%2BPHBhdGggZD0iTTQgMjBoMTYiLz48L3N2Zz4%3D&logoColor=white&labelColor=9a3412)](https://lichiphen.com/tool/link/r/NagiManga)

```
nagimanga/
├── read.php          読者向けの窓口（読み取り専用）
├── index.php         NagiLog の公開入口（URLにファイル名は不要）
├── log.php           LOG本体・以前のURLから新URLへの転送
├── .htaccess
├── admin/            管理画面
├── viewer/           ビューアー（NagiManga.js / NagiManga.css）
├── plugins/          プラグインを置く場所（最初は空）
├── lib/              プログラム本体（外からは見えません）
└── data/             作品の画像と設定（外からは見えません）
```

- `data` フォルダは書き込みできるようにしてください（多くのサーバーでは 755 のままで大丈夫です）。

> [!WARNING]
> **`.htaccess` を忘れずに送ってください。** FTP ソフトによっては、`.` で始まるファイルが隠れていて送られないことがあります（「隠しファイルを表示」をオンにすると見えます）。`nagimanga/`・`admin/`・`lib/`・`data/`・`plugins/` の 5 か所にあります。

### 2. セットアップ

1. ブラウザで `https://あなたのサイト/nagimanga/admin/` を開きます。セットアップの画面が出ます。**このとき初めて、サーバーが `data/setup-key.php`（セットアップの鍵）を作ります。** アップロードしただけでは、まだありません。
2. FTP（またはサーバーのファイルマネージャー）で `data/setup-key.php` を開きます。FTP ソフトに出てこないときは、一覧を更新（再読み込み）してください。開き方は、セットアップ画面の「セットアップの鍵の見つけ方」にも書いてあります。
3. 書いてある「セットアップの鍵」（`ABCDE-FGHJK-LMNPQ-RSTUV` のような 20 文字）を、画面の欄に貼り付けます。小文字や空白が混ざってもかまいません。

   ![setup-key.php をメモ帳などで開いたところ。1 行目は PHP の行、3 行目が鍵（例: ABCDE-FGHJK-LMNPQ-RSTUV）、その下に日本語と英語の説明](docs/images/setup-key.png)

   ファイルを開くと、このように書いてあります。3 行目の `XXXXX-XXXXX-XXXXX-XXXXX` の形の行が鍵です（鍵はサイトごとに違います）。
4. 管理者パスワード（10 文字以上）を決めます。自動では作られないので、自分で決めてください。
5. 「おもに使うもの」を、大きなボタンで **NagiLog**（日記・ブログ・写真）か **NagiManga**（漫画）から選びます。ログインしたときに最初に開く画面です。どちらを選んでも両方使えて、ログイン画面でいつでも切り替えられます。

- 「今の IP アドレスからだけ管理画面を開けるようにする」は、できるだけオンにしてください。

![セットアップの画面。セットアップの鍵・管理者パスワードの欄と、NagiLog か NagiManga を選ぶ大きなボタン](docs/images/setup.jpg)

> [!IMPORTANT]
> 鍵は設置した人だけが FTP で読めます（Web からは読めません）。アップロード直後のサイトを他人やボットが先に開いても、鍵を知らなければセットアップできません。設定が終わると `data/setup-key.php` は自動で消えます。v0.11 までの「30 分以内」の制限はなくなりました。

> [!IMPORTANT]
> 完了すると **ログイン用の URL** が表示されます。**必ずブックマークしてください。** 管理画面を直接開こうとすると「Not Found」になります。v0.10.0 からは、セットアップで「秘密のログイン URL からだけログインできるようにする」が最初からオンです。このとき `admin/login.php` と、URL の `k=` を削って開いたページは「Not Found」になり、LOGに「ログイン」のリンクも出ません。設定 → 「共通・安全」 → 「ログインの入口」で、LOGの「ログイン」（`admin/login.php`）からも入れるように戻せます（v0.9 までに設置したものは、こちらのままです）。IP制限と試行回数の制限は、どちらの入口にも効きます。
>
> ログイン画面では、ログインしたあとに開く画面（NagiLog か NagiManga）を大きなボタンで選べます。選んだものは次回も選ばれています。初めてのブラウザーでは、セットアップの「おもに使うもの」が選ばれています。ログイン URL の後ろに `&p=log` を付けた「NagiLog 用」の URL は、最初から NagiLog が選ばれた状態で開きます（完了の画面と、設定の「ログイン URL」の欄に表示します）。

#### 初めてログインしたら：二段階認証

パスキーも二段階認証（認証アプリの確認コード）もまだのうちは、ログインするたびに、どちらかを足すようすすめるダイアログが出ます。パスワードが知られても、パスキーや確認コードがなければログインされません。できるだけ、ここで設定してください。

- **パスキーを登録する**：指紋・顔・PIN でログインできるようにします。
- **認証アプリを設定する**：iPhone の「パスワード」アプリや Google Authenticator などの 6 桁の確認コードを使います。
- **あとで**：今回は閉じます。次にログインしたときに、また出ます。
- **今後は表示しない**：二度と出しません。あとからでも、設定の「二段階認証」「パスキー」から設定できます。

![ログインのあとに出るダイアログ。「パスキーを登録する」「認証アプリを設定する」のボタンと、「あとで」「今後は表示しない」](docs/images/setup-twofa.jpg)

### 3. 設置したら確認すること

- ブックマークした URL でログインし、作品一覧に **「data フォルダがインターネットから見える状態です」という警告が出ていない** こと（出ていたら `.htaccess` が届いていないか、効いていません。下の「data フォルダを守る」を見てください）
- `https://あなたのサイト/nagimanga/data/` を開くと、中身ではなく「Forbidden」などのエラーになること
- `https://あなたのサイト/nagimanga/admin/` をそのまま開くと「Not Found」になること（セットアップが済んでいる証拠です）

### 4. 作品を作って共有する

1. 管理画面で作品を作り、ページ画像をまとめてドラッグ＆ドロップします（ファイル名の順 001, 002, … に並びます）。EPUB がある場合は「EPUB から作る」を使います。
2. 作品ページの「共有用のタグ」で読み方・見開きを選び、「コピー」してブログの HTML に貼ります。

```html
<a href="#" data-nagimanga="Ab3dE5gH7jK9" data-endpoint="https://example.com/nagimanga/read.php"
   data-direction="rtl" data-view="auto" data-cover="1">第1話を読む</a>
<script src="https://example.com/nagimanga/viewer/NagiManga.js?v=..." defer></script>
```

| 属性 | 値 |
|---|---|
| `data-direction` | `rtl`（右から左）/ `ltr`（左から右）/ `vertical`（縦読み） |
| `data-view` | `auto`（横長の画面なら見開き）/ `single`（常に 1 ページ） |
| `data-cover` | `1`（1 ページ目は単独＝奇数ページ始まり）/ `0`（1 ページ目から見開き） |
| `data-vertical` | 縦読みのとき: 省略（ページ漫画: 1 ページが画面の縦に収まる）/ `webtoon`（横幅いっぱい、最大 900px） |

- `<script>` の行は 1 ページに 1 回で十分です。リンクはいくつ並べても構いません。
- タグの URL は、管理画面を開いているアドレスから自動で作られます。別の URL にしたいときだけ「設定 → 設置 URL」に入力してください。
- **別のサイト（ドメイン）に埋め込むとき** は、「設定 → 別のサイトに埋め込む場合」にそのサイトのアドレス（例: `https://blog.example.com`）を登録してください。

### てがろぐに載せる

てがろぐの投稿には、ブログ用の HTML タグは使えません。代わりに、作品ページのいちばん上にある **「共有リンク」** を使います。

1. てがろぐの管理画面で **[設定] → [システム設定] → 【画像拡大スクリプトの選択】** を開き、「**他のスクリプトを使う：URLを指定**」を選びます。
2. 「JavaScriptのURL」欄に、NagiManga の URL を書いて保存します。管理画面の **「設置用コード」** に、この欄にそのまま貼れる 1 行（NagiSwipe と NagiManga の両方、キャッシュバスター付き）があります。
   画像の拡大表示に NagiSwipe を使っている場合は、NagiSwipe の URL の **うしろに半角スペースを 1 つ入れて** 続けて書きます（1 行のまま続けます）。

   ```
   https://cdn.jsdelivr.net/gh/Lichiphen/NagiSwipe@83d6ea2/NagiSwipe-main.js https://example.com/nagimanga/viewer/NagiManga.js
   ```

   ※ URL の `@` の後ろ（`@93db749` など）は、ファイルのバージョンを表す値です。NagiSwipe が更新されると、README の値も新しくなります。新しい版を使いたいときは、ここの URL を貼り直してください（貼り直すまでは、今の版のまま表示が変わりません）。 NagiManga を更新したときも、「設置用コード」の新しい 1 行に貼り替えてください。

3. **画像のない投稿でも** 漫画を開けるように設定します。てがろぐは、画像のない投稿のページでは上の欄のファイルを読み込まないためです。[設定] → [ページの表示] → 【投稿本文の表示／URL処理】 の「▼画像URLを画像として埋め込む表示」にある **「画像リンクに独自のclass属性値を追加する」** にチェックを入れ、`class="` と `"` の間の欄に `nagimanga` と入力して保存します。
4. NagiManga の管理画面で作品を開き、「共有リンク」の「コピー」を押します。
5. てがろぐに、リンクの文字（下の例では「第1話を読む」）を付けて投稿します。

   ```
   [第1話を読む]https://example.com/nagimanga/read.php?nagimanga=Ab3dE5gH7jK9&dir=rtl
   ```

投稿の「第1話を読む」を押すと、その場で漫画が開きます。読み方や見開きの選び方も、この URL の中に入っています。

- この URL は、RSS リーダーなど、ほかの場所で開いても大丈夫です（作品を読むためのページが開きます）。
- てがろぐと NagiManga のアドレスの始まり（`https://〜/` の部分）が違う場合は、「設定 → 別のサイトに埋め込む場合」に、てがろぐのアドレスを登録してください。
- HTML を書けるブログなら、今までどおり「共有用のタグ」を使ってください。

---

## 機能の説明

### EPUB から作る

CLIP STUDIO PAINT などで書き出した **漫画の EPUB**（固定レイアウト＝画像のページだけのもの）を、作品一覧の「EPUB から作る」で取り込めます。

- ページの順番・読む向き（右から左／左から右）・タイトルは EPUB の設定をそのまま使います（タイトルは入力すれば置き換えられます）。
- 「小容量版 EPUB（任意）」も一緒に選ぶと、同じ順番で小容量版として割り当てます。あとから作品ページの「小容量版 EPUB から割り当てる」でも追加できます。
- 画像は通常のアップロードと同じく、中身を確かめてから描き直して保存します。見開きをそろえるための白紙ページもそのまま残ります。
- DRM（コピー防止）付きの EPUB、文章だけの EPUB は読めません。
- サーバーの「アップロード上限」（`upload_max_filesize` / `post_max_size`）より大きい EPUB は取り込めません。上限は管理画面に表示されます。

### 小容量版（スマホ用）

各ページに、小さく書き出した「小容量版」を付けられます。作品ページの「ページ」欄には、アップロードする場所が 2 つあります。

- **通常版（パソコン・拡大用）**: いつものページ画像
- **小容量版（スマホ用・任意）**: 同じページを小さくした画像。ファイル名の順に、1 ページ目から順番に割り当てます（001 → 1 ページ目）。枚数がページ数と違うときは確認が出ます

ビューアーは、表示する大きさと画面の細かさから「小容量版で足りる」ときは小容量版を読み、パソコンの大きな画面や拡大したときは通常版に切り替えます（裏で読み込んでから差し替えるので、一瞬白くなることはありません）。端末の「データセーバー」がオンなら小容量版を優先します。小容量版がないページは通常版だけで表示します。

- 小容量版があるページには、作品ページの一覧で「小」の印が付きます
- 「小容量版をすべて外す」で外せます（通常版は残ります）
- パスワード付き作品では、小容量版も鍵がないと読めません
- バックアップ・復元にも含まれます

### パスワード（限定公開）

作品ページの「パスワード」で設定します。読む人はリンクをクリックしたときにパスワードを入力します。

- パスワードは **ビューアーを開くたびに** 入力します（正しく入力すると、開いている間だけ有効な「閲覧用の鍵」が渡され、閉じると捨てられます。鍵そのものも 2 時間で無効になります）。
- パスワードを変えると、それまでの鍵はすべて無効になります。
- 設定したパスワードは、作品ページの「現在のパスワード」で「表示」を押すと確認できます（サーバーの秘密鍵で暗号化して保存しています。別のサーバーへ復元した場合は表示できないので、設定し直してください）。
- 5 回間違えると、その作品は 15 分間入力できなくなります（総当たり対策）。

### バックアップ

「バックアップ」から、すべての作品を 1 つの ZIP にまとめてダウンロードできます。定期的にダウンロードしてパソコンなどに保存しておいてください。

- 作品（ページ画像・小容量版・並び順・タイトル・読む向き・読者向けパスワード）が入ります。作品の ID も変わらないので、ブログに貼ったタグはそのまま使えます。
- 管理者パスワード・ログイン URL・IP 制限などの設定と、秘密の鍵は入りません（引っ越し先ではセットアップし直します）。
- 復元は同じ画面から ZIP をアップロードします。ZIP の中身はすべて検査してから戻します。
- サーバーの PHP に ZipArchive がない場合は、FTP で `data/works` フォルダをまるごとコピーしてください。

### 更新（v0.2.0 以降）

新しいバージョンが出ると、ログインしたときに作品一覧と「LOG・投稿」の上にお知らせが出ます（メニューの「更新」にも「新」の印が付きます）。「更新」画面のボタンを押すと、その場で更新できます。更新画面では、リリースの内容（見出し・箇条書き・太字・リンク）を整えて表示します。

- GitHub の公式リリースから `nagimanga-vX.Y.Z.zip` をダウンロードし、GitHub が公開している確認用の値（SHA-256）と一致したときだけ使います。中身も 1 つずつ検査します。
- v0.12.0 からは、配布元（Lichiphen）の署名（Ed25519。リリースに付けた `nagimanga-vX.Y.Z.zip.sig`）も確かめます。署名に使う鍵は作者の PC にだけあり、GitHub には置いていません。GitHub のアカウントやリリースが乗っ取られて中身を差し替えられても、署名が合わないので更新しません。PHP に sodium がないサーバーでも、NagiManga が自分で確かめます。
- 入れ替えるのはプログラムのファイルだけです。作品・設定（`data` フォルダ）、プラグイン、`lib/paths.php` はそのままです。
- 入れ替える前のファイルは `data/update/` に保管され（実行できない `.bak` として）、「元に戻す」で戻せます。直近 3 回分を残します。
- 確認は 12 時間に 1 回までです。「更新」画面で、ログイン時の確認をオフにしたり、今すぐ確認したりできます。
- サーバーから GitHub に接続できない（PHP の curl も allow_url_fopen も使えない）、ZipArchive がない、プログラムのファイルに書き込めない場合は自動で更新できません。画面の「手動で更新する」の手順（`data` フォルダ以外を上書きアップロード）で更新してください。
- v0.1.0 から v0.2.0 へは、手動で更新してください（v0.1.0 には更新機能がありません）。

### 管理画面の言語

管理画面は、日本語と英語で表示できます。作品一覧・作品ページ・LOG・投稿・カテゴリ・固定ページ・画像一覧・設定・バックアップ・更新・ログイン画面と、保存のときのメッセージが対象です。

- 「設定 › 共通・安全 › 管理画面の言語」で選びます。最初は「自動」で、ブラウザの言語が対応している言語ならその言語、そうでなければ日本語で表示します。
- 「English」を選ぶと、公開ページ（LOG・作品ページ・404・鍵や閲覧注意の画面・共有カードの文字）も英語になります。「自動」はブラウザーごとに言語が変わるため、全員に同じページを返す公開ページは日本語のままです。
- 投稿の本文やカテゴリ名など、自分で書いた内容は翻訳しません。フッターの文と閲覧注意の定型文は、最初のままなら英語になります。

**言語を足すとき**: `server/lib/lang/` に `fr.json` のような JSON を置くと、設定の選択肢に出ます（中国語なら `zh-CN.json` のように地域を付けても使えます）。

- 中身は `{"日本語の文": "訳文"}` の組です。プログラムの中の日本語の文をそのまま鍵にしています。`"_name"` に、選択肢に出す言語名（例: `"Français"`）を書きます。`_` で始まる鍵は翻訳者向けのメモで、画面には出ません。
- `{n}` のような差し込みは、そのまま訳文に残してください。訳文には `'` `"` `&` `<` `>` を使わず、アポストロフィは `’` にします。
- 訳がない文は日本語のまま出ます。`python dev/i18n_test.py` で、訳し漏れ・差し込みの違い・使えない文字を確かめられます。
- 新しく画面の文を足すときは、PHP なら `nm_t('文')`、JS なら `t('文')` を通し、`lib/lang/` の各 JSON に訳を足します。数字や名前が入る文は `nm_t('{n}件の記録', ['n' => $count])` のように、文をまるごと鍵にします。

### 設置用コード

メニューの「設置用コード」に、ビューアーを読み込むためのコードがまとまっています。

- ビューアーの URL（末尾の `?v=…` が **キャッシュバスター**。NagiManga を更新すると値が変わり、読む人のブラウザに古いビューアーが残らないようにします）
- てがろぐの「JavaScriptのURL」欄にそのまま貼れる 1 行（NagiSwipe と一緒）と、てがろぐ側の設定の手順
- ブログ・HTML 用の `<script>` タグ

**NagiManga を更新したら、この画面を開き直して、新しい値を貼り直してください。**

### 個別ページ（共有リンク・v0.3.0 以降）

作品ページのいちばん上の **「共有リンク」**（`read.php?nagimanga=…`）を開くと、その作品を読むためのページが開き、ビューアーが自動で開きます。

- note・アメブロ・Instagram・X など、漫画を埋め込めない場所にはこのリンクを貼ります。貼ると **表紙付きのカード**（OGP）で表示されます。パスワード付きの作品は表紙を出さず、NagiManga の共通の画像になります。
- ページには表紙・タイトル・「読む」ボタンと、**「戻る」ボタン** があります。行き先は「設定 → 個別ページ」で決められます（空欄なら自動：来たページに戻ります。Instagram などのアプリから来て戻る先がわからないときは、ボタンを出しません）。
- 作品ページの「非公開にする」で、作品ごとに個別ページを出さないようにできます。ブログやてがろぐに埋め込んだビューアーは今までどおり読めます。
- 作品の ID は推測できないランダムな文字列です。すでにある作品もそのまま使えます（作り直しは不要です）。

### 直リンク防止（上級者向け・v0.3.0 以降）

「設定 → 上級者向けの設定 → 直リンク防止」をオンにすると、漫画の画像は「このサイト」「別のサイトに埋め込む場合に登録したサイト」「許可リストに書いたサイト」のページからしか読めなくなります。ほかのサイトに画像を貼られてサーバーの通信量が増えるのを防ぎます。初期設定はオフです。

- `.htaccess` は書き換えません（判定は read.php の中で行うので、nginx のサーバーでも使えます）。
- 共有リンクの個別ページと、リンクのカード用の表紙は、オンでもそのまま使えます。
- 許可リストはサイト単位（例: `https://note.com`）で書きます。ブラウザは、ほかのサイトからの読み込みでは URL の途中（`/ユーザー名/` など）を送らないため、途中までの一致では判定できません。

**LOGの画像・動画・音声（v0.12.0 以降）**: 同じ場所の「LOGの画像・動画・音声に直リンク防止を使う」は、漫画とは別のスイッチです。オンにすると、ほかのサイトのページや掲示板に貼られた LOG の画像・動画・音声を表示しません。無駄な通信量を防げます。

- **v0.12.0 から新しく設置したサイトでは、最初からオンです。** それより前に設置したサイトは、更新してもオフのままです。LOG の管理画面と作品一覧に出るお知らせの **「オンにする」** を押すだけでオンにできます（「今はしない」でお知らせは消えます）。
- URL を X や LINE などに貼ったときのリンクのカード（OGP）の画像は、これまでどおり表示します。カードを作るサービスは、ページを通さずに画像を取りに来るためです。URL を直接開いたとき、RSS リーダーの取得も同じです。
- **OGP 画像（リンクのカードに使う画像）は、直リンク防止の対象外です（v0.12.1 以降）。** 共通の OGP 画像と、記事のカードに使う画像（記事の最初の画像、動画・音声ならジャケット）は、ほかのサイトのページからも表示します。OGP チェッカーや、紹介記事のブログカードのように、ページの中にカードの画像を直接出すサイトでも画像が出ます。サムネイルと、記事の2枚目からの画像は対象のままです。
- LOGのページ、LOGを埋め込んだページ（`embed.php`）、許可リストのサイトからは表示します。
- **相互リンクのバナーなど、ほかのサイトに貼ってもらいたい画像** は、画像一覧でその画像の「ほかのサイトにも貼れるようにする」にチェックを入れて保存します。その画像だけ、直リンク防止の対象外になります。画像は、公開している記事か固定ページ（リンクのページなど）に載せておいてください。
- 直リンク防止の対象の画像は、ブラウザーだけに保存させます（`Cache-Control: private`、v0.12.1 以降）。CDN などの共有キャッシュに置かれて、ほかのサイトのページにそのまま渡ってしまうのを防ぐためです。漫画の画像も、漫画の直リンク防止がオンの間は同じです。
- Feedly のように、ブラウザーの中で画像を表示する RSS リーダーで画像を出したいときは、そのサイト（例: `https://feedly.com`）を許可リストに書きます。

### アクセス解析（v0.13.0 以降）

「設定 → SEO・アクセス解析 → アクセス解析」に、Matomo・Google アナリティクス・Microsoft Clarity・Plausible・Cloudflare Web Analytics などの計測サービスに表示されたコードを、`<script>` タグごと貼り付けて保存します。公開LOGと漫画の作品ページ（`read.php?nagimanga=…`）に入ります。空欄で保存すると使いません。

- **閲覧者に害のあるコードは保存しません。** 保存するときに中身を確かめ、見つかったときは理由を画面の下にエラーのトーストで出します（どの設定も保存しません）。拒否するのは、Cookie・ブラウザーの保存データ・フォームの入力欄を読むコード、キー入力を見張るコード、ほかのページへ移動させるコード、ページを書き換えるコード（`document.write`・`innerHTML` など）、自分で通信するコード、`eval` などで文字列を実行するコード、難読化したコード、マイニングなどです。`<script>` 以外のタグ、`onload` などの属性、`http://` からの読み込みも入れられません。
- 公開ページの CSP（Content-Security-Policy）はそのまま厳しく保ち、**コードに書かれたサイト（と、Google アナリティクスや Clarity が続けて使うサイト）だけ** を、スクリプト・通信・画像に許可します。貼ったコードの中身は、CSP にハッシュで登録します。
- `<noscript>` の部分（JavaScript を使わない人向けの計測）は入れません。
- ログイン中の自分が見る LOG では計測しません。管理画面にも入れません。
- 設定ファイル（`data/config.php`）を直接書き換えた場合も、ページを出すたびに同じ確認をして、通らないコードは出しません。

同じタブの「検索エンジンの所有権の確認」には、Google Search Console・Bing Webmaster Tools・Yandex Webmaster・Naver Search Advisor・百度（Baidu）站长平台の「HTML タグ」の確認方法で表示される meta タグをそのまま貼れます（確認コードだけでも可）。LOGのすべてのページの head に入ります。ほかのサービスのタグや、形の合わないコードは保存しません。「検索エンジンとサイトマップ」もこのタブにあります。

### プラグイン（ゲストモード）

`nagimanga/plugins/` フォルダに PHP のプラグインを置くと、機能を足せます（置いたファイルだけが読み込まれます）。同梱のプラグインは次の 1 つです。

**[`plugins/guest-mode.php`](plugins/guest-mode.php)（ゲスト・閲覧のみ）**: デモなどで、訪問者に管理画面の中を見せたいときに使います。このファイルを `nagimanga/plugins/` にコピーすると、`admin/index.php?guest` からゲストとして入れるようになります。

- ゲストは作品・ページ・共有タグ・試し読みを見られますが、アップロード・並べ替え・削除・パスワード・小容量版・EPUB・バックアップ・設定の変更はすべて拒否されます。設定画面（ログイン URL・IP・ログ）と読者向けパスワードは表示されません。
- この制限はプラグインではなく NagiManga 本体が行っています。プラグインが決めるのは「入口を開けるか」「合言葉」「案内の文」「終了後のリンク先」だけです。
- あなた自身のログイン（秘密の URL ＋ パスワード ＋ IP 制限）は今までどおりです。
- ファイルを消せば入口は閉じ、中にいたゲストも締め出されます。

### 二段階認証（v0.9.1 以降・任意）

ログインのときに、パスワードに加えて、スマホの認証アプリに表示される 6 桁の確認コードを入力するようにできます。パスワードが知られても、スマホがなければログインできません。初期設定はオフです。

1. 設定 → 「共通・安全」 → 「二段階認証を設定する」を開きます。
2. 認証アプリで、画面の QR コードを読み込みます。iPhone は標準の「パスワード」アプリ（iOS 18 以降。iOS 17 以前は「設定」→「パスワード」）、ほかに Google Authenticator・Microsoft Authenticator・1Password などが使えます。
   - スマホでこの画面を開いているときは「このスマホの認証アプリに登録する」を押すと、認証アプリが開きます。
   - 読み込めないときは、画面の鍵（英数字 32 文字）を手で入力します。
3. アプリに出た 6 桁の確認コードと管理者パスワードを入れて「確認してオンにする」を押します。
4. **リカバリーコード（8 個）は一度だけ表示します。** 必ず保存してください。スマホをなくしたときに、確認コードの代わりに 1 個ずつ使えます。

- 確認コードは 1 回しか使えません（同じコードでもう一度ログインはできません）。スマホの時計が 30 秒ほどずれていても受け付けます。
- 確認コードの欄には半角数字しか入りません。全角の数字（日本語キーボード）は半角に直し、英字・記号・間の空白は除きます。パソコンで日本語入力がオンのままでも、変換中の文字は残らず半角数字になります。
- 確認コードの間違いは、パスワードの間違いと同じように数えます（初期設定では 5 回で 15 分ロック）。
- オン・オフを切り替えると、ほかの端末はログアウトされます。
- 鍵は `data/config.php` に暗号化して保存します。
- アプリには「NagiSeries NagiLog」の名前で、サイトのドメイン（例: `example.com`）をアカウント名として登録されます。名前を変えても確認コードは変わらないので、前に登録したものもそのまま使えます。画像の欄（`image=`）に対応したアプリには、LOGのファビコン（設定していなければ NagiManga のアイコン）も渡します。Authy など、名前から自分でロゴを探すアプリでは関係のないロゴが出ることがあるので、そのときは汎用のロゴ（鍵のマーク）を選んでください。

> [!NOTE]
> QR コードはこのプログラムの中（`lib/qrcode.php`）で作ります。鍵をほかのサービスへ送ることはありません。QRコードは株式会社デンソーウェーブの登録商標です。

### パスキー（v0.10.0 以降・任意）

Proton Pass、iPhone の「パスワード」アプリ（iCloud キーチェーン）、Google パスワードマネージャー、1Password、Windows Hello などに保存したパスキーで、パスワードを入れずにログインできます。

1. 設定 → 「共通・安全」 → 「パスキーを登録する」を開きます。
2. 名前（例: iPhone、Proton Pass）と管理者パスワードを入れて「パスキーを登録する」を押すと、ブラウザが保存先を聞いてきます。
3. 次からは、ログイン画面の **「パスキーでログイン」** を押して、指紋・顔・PIN で確かめるだけでログインできます。

- **HTTPS のアドレスでしか使えません**（`https://〜`。確認用の `http://localhost` は例外）。
- パスキーは **登録したときのドメインに結び付きます**。ドメインを変えたときは登録し直してください。
- パスキーは、端末を持っていることと本人確認（指紋・顔・PIN）の両方を兼ねるので、パスキーでログインしたときは **パスワードと二段階認証の確認コードは聞きません**。パスワード（と確認コード）でのログインも、これまでどおり使えます。
- 10 個まで登録できます。削除には管理者パスワードが要ります。
- 失敗はパスワードの間違いと同じように数えます（初期設定では 5 回で 15 分ロック）。
- 保存するのは公開鍵だけです（`data/config.php`）。秘密の鍵はパスキーの保存先から出ません。
- 外部のライブラリやサービスは使わず、PHP の openssl で確かめます（ES256・RS256 に対応）。

### 困ったとき

| 症状 | 対処 |
|---|---|
| セットアップの鍵が分からない | FTP で `data/setup-key.php` を開く（ないときは、管理画面をもう一度開くと作られます） |
| ログイン URL を忘れた（秘密のログイン URL だけにしているとき、ほかに入口はありません） | FTP で `data/config.php` を開き、`login_key` の値を `admin/index.php?k=` の後ろに付けて開く |
| IP アドレスが変わって締め出された | FTP で `data/config.php` の `allowed_ips` を `array ( )` にする（`admin/.htaccess` で IP 制限を書いた場合はその行も） |
| ログインを何度も間違えた | 設定したロックの時間（初期設定は 15 分）待つ。その間はログイン画面も「Not Found」になります。前にログインしたブラウザーやパスキーなら入れます。急ぐときは、FTP で `data/ratelimit` フォルダの中身を消す |
| 「ログインの失敗が多いため…」と出てパスワードで入れない | ほかの人の失敗がサイト全体で多いときの表示です。前にログインしたブラウザー、パスキー、許可した IP アドレスからなら入れます。最長 1 時間待つか、FTP で `data/ratelimit` フォルダの中身を消す |
| 「署名がない」「署名が一致しない」と出て更新できない | 配布元の署名を確かめられなかったため、安全のため止めています（何も変わっていません）。GitHub のリリースが直るのを待つか、公式のリリースの ZIP を手動で上書きアップロードしてください |
| 二段階認証のスマホをなくした | 確認コードの欄に、保存しておいたリカバリーコードを入れる。リカバリーコードもないときは、FTP で `data/config.php` を開き、`'totp' =>` から始まる項目を消す（二段階認証がオフに戻ります） |
| パスキーを使えない・なくした | パスワード（と確認コード）でログインし、設定 → 「パスキー」で削除・登録し直す。すべて消すには、FTP で `data/config.php` を開き、`'passkeys' =>` から始まる項目を消す |
| 管理画面に「data フォルダがインターネットから見える状態です」と出る | 下の「data フォルダを守る」を参照 |
| EPUB が取り込めない | DRM 付き・文章だけの EPUB ではないか、アップロード上限（管理画面に表示）を超えていないかを確認 |

### data フォルダを守る

`data` フォルダは `.htaccess` で外から見えないようにしています。`.htaccess` が効かないサーバー（nginx など）では、次のどちらかをしてください。

1. **おすすめ:** `data` フォルダを公開フォルダの外（例: `/home/you/nagimanga-data`）へ移し、`lib/paths.php` を作って場所を書く

   ```php
   <?php return '/home/you/nagimanga-data';
   ```

2. nginx なら `location ~ /nagimanga/(data|lib|plugins)/ { deny all; }` を設定する

> [!CAUTION]
> 保存フォルダの名前は秘密の鍵から作りますが、暗号化ではありません。`.htaccess` が効かず画像の実パスが判明した場合は、画像へ直接アクセスできてしまいます。保存先を公開フォルダの外へ移すか、サーバー側でアクセスを拒否してください。

- 設定ファイルは PHP なので、中身は表示されません。
- 作品フォルダの名前は秘密の鍵から作ってあり、公開 ID からは推測できません。
- 各フォルダには空の `index.html` を置いてあり、中身の一覧は表示されません。

LOGの内部記録、セキュリティログ（`data/logs/security.log.php`）、セットアップの鍵（`data/setup-key.php`）、更新の確認の記録（`data/update/check.php`）は、直接開くと404になるPHP形式です。静的な画像の保護には、上記の保存先・サーバー設定が必要です。

**`.htaccess` が効かないときに守れる範囲（v0.12.0 以降）**

| | `.htaccess` が効かなくても | 守るには |
|---|---|---|
| 設定・パスワード・秘密の鍵・ログ・セットアップの鍵・LOGの記事 | 読まれません（PHP 形式で、開くと 404） | ― |
| 作品と LOGの画像・動画 | 保存フォルダの名前は推測できませんが、実際の URL が知られると、閲覧パスワードや直リンク防止を通らずに開けます | 保存先を公開フォルダの外へ移すか、サーバー側で拒否する（上の 1 か 2） |
| LOGのきれいな URL（`/20261027123456`・`/privacy`）、404 ページ、キャッシュの設定 | 使えません（LOGは自動で `?id=`・`?pg=` の URL に切り替えるので、記事は開けます） | Apache 系のサーバーで `.htaccess` を送る |

Apache 系のサーバー（ほとんどのレンタルサーバー）では、`.htaccess` を送れば、この表の守りがすべて効きます。

### 管理画面の IP 制限をさらに固くする（任意）

`admin/.htaccess` の IP 制限の行の `#` を外して、自分の IP アドレスを書きます。管理画面の設定の IP 制限と二重に守られます。

---

## PHP なし（FTP だけ）で使う

1. `server/viewer/NagiManga.js` と `NagiManga.css` を同じフォルダに置きます。
2. [`tools/manifest-maker.html`](tools/manifest-maker.html) をブラウザで開き、ページ画像を選んで `manifest.json` を作ります（画像はどこにも送信されません）。
3. `manifest.json` と画像をサーバーに置き、ページに次のタグを貼ります。

```html
<a href="#" data-nagimanga-manifest="/manga/ep1/manifest.json" data-direction="rtl">第1話を読む</a>
<script src="/manga/NagiManga.js" defer></script>
```

`manifest.json` の中の画像の場所は、`manifest.json` の場所から見た相対パス（または完全な URL）です。

```json
{ "title": "第1話", "direction": "rtl", "pages": [ { "src": "001.jpg", "w": 1200, "h": 1700 } ] }
```

---

## セキュリティの考え方

- **公開側は読み取り専用。** `read.php` ができるのは「作品のページ一覧」「パスワードの確認」「画像を返す」だけです。おかしな要求にはすべて同じ「Not Found」を返し、何があるかを教えません。
- **管理画面は 5 重の鍵。** IP 制限 → 秘密のログイン URL → パスワード（失敗の回数とロックの時間は設定で変えられます。初期設定は 5 回で 15 分。全体でも 1 時間 40 回まで。オンにすれば、続けて認証アプリの確認コード。登録すれば、代わりにパスキー）→ ブラウザに結び付いたセッション（最後のログインから 30 日、設定で 1 年まで）→ すべての操作に CSRF 対策。
- **他人の失敗で持ち主が締め出されない。** 一度ログインしたブラウザーには「持ち主の印」（サーバーの秘密の鍵で署名した Cookie）を渡します。印のあるブラウザー、パスキー、許可した IP アドレスは、全体の上限（1 時間 40 回）に達しても入れます。印のあるブラウザーは IP ではなくブラウザーごとに数え、5 回間違えると印が効かなくなります。パスワードを変えると、すべての印が無効になります。
- **IPv6 はまとめて数える。** 失敗の回数は、IPv6 なら先頭の /64（ひとつの回線が持つアドレスの範囲）を一人として数えます。作品の閲覧パスワードと鍵付きの記事は、作品・記事ごとに全員合わせて 1 時間 60 回を超えると、そこからは 20 秒に 1 件ずつだけ確かめます（v0.12.1 以降。それより前は 1 時間止めていました）。初めての読者も少し待てば開けて、1 時間締め出されることはありません（混み合っているときは「1分ほどしてから」と出ます。すでに開けた読者はそのまま読めます）。
- **アップロードされた画像は必ず描き直す。** ファイル名ではなく中身で画像かどうかを判定し、GD で読み込んで新しいファイルに書き出します。画像に仕込まれたプログラムや撮影場所などの情報は残りません。ファイル名もこちらで付け直します。SVG は受け付けません。EPUB やバックアップの ZIP も、中身を 1 つずつ検査してから使います。
- **画像の保存フォルダではプログラムが動かない。** `data` は外から見えず、保存するのは `.webp` / `.jpg` だけです。
- **エラー内容を画面に出さない。** サーバーのパスなどが漏れないようにしています。記録は `data/logs/security.log.php` に残り、管理画面の設定ページで見られます。PHP の形にしてあるので、`.htaccess` を読まないサーバー（nginx など）でも Web から中身は読めません。

### 攻撃テスト

[`dev/attack_test.py`](dev/attack_test.py) で、通常の動作確認と攻撃の再現をまとめて実行できます。Python 3 と PHP 8.1 以上（GD・fileinfo・zip・mbstring・sodium または openssl）があれば、追加のインストールは不要です。LOGの確認は [`dev/log_test.py`](dev/log_test.py) で実行できます。

```bash
python dev/attack_test.py
```

<details><summary>攻撃テストで確かめていること</summary>

主な内容: 画像に偽装した PHP、`.htaccess` / SVG / HTML のアップロード、ピクセル数の爆弾、`../` によるフォルダの抜け出し、ZIP スリップと ZIP 爆弾、悪意のある EPUB（XXE・DRM・本の外を指すパス）、CSRF、XSS、閲覧用の鍵の偽造・改ざん・期限切れ・使い回し、総当たり（`X-Forwarded-For` の偽装を含む）、IP 制限の回避、セットアップの鍵、署名のない・差し替えた更新ファイル、おかしな Host ヘッダー、ゲストによる変更（19 種類）、`.htaccess` が効かないサーバーでの情報漏れ。結果は `dev/results/attack-report.md` に書き出されます。

[`dev/hardening_test.py`](dev/hardening_test.py) は、多くの IP アドレスからの攻撃（`X-Forwarded-For` で再現）を試します。全体の上限があっても持ち主が締め出されないこと、IPv6 を /64 で数えること、作品ごとの上限、ログイン中の画面からのパスワードの当てずっぽう、`.htaccess` がないときのログです。

</details>

## 開発

```bash
dev\serve.cmd              # http://127.0.0.1:5190 （データは dev/data）
python dev/seed.py         # 見本の作品を登録し、dev/results/demo.html を作る
python dev/build_release.py  # 配布用 ZIP（dev/results/nagimanga-vX.Y.Z.zip）を作る
python dev/i18n_test.py    # 管理画面の訳（server/lib/lang/*.json）に漏れがないか確かめる
python dev/hardening_test.py  # 締め出し・IPv6・作品ごとの上限・ログの守りを確かめる
python dev/release_sign.py keygen  # リリースの署名の鍵を作る（最初の 1 回だけ。鍵はリポジトリの外）
python ../site/build.py    # ドキュメントサイトを README から作り直す
```

[ドキュメントサイト](https://nagiswipe.pages.dev/) は、リポジトリ直下の README.md・manga/README.md・manga/README-LOG.md・manga/docs/overview.md と、それぞれの英語版（`.en.md`）から作ります。トップページの文章は `site/top.html` と `site/top.en.html`、見た目は `site/site.css` です。README を直したらページを作り直し、一緒にコミットしてください。サイトのファイルは、配布 ZIP や GitHub のソースコードの ZIP には含めません。

README の jsDelivr の URL は、そのファイルを最後に変更したコミットに固定しています（例: `…/NagiSwipe@93db749/NagiSwipe-main.js`）。コミットの URL は中身が変わらず、ファイルを変えるたびに URL も変わるので、ブラウザや jsDelivr に古いファイルが残りません。`cp scripts/post-commit.sh .git/hooks/post-commit` でフックを入れておくと、NagiSwipe-main.js / .css を変更したコミットのあとに README の URL を自動で書き換え、[ドキュメントサイト](https://nagiswipe.pages.dev/) のページ（`python site/build.py`）も作り直してコミットします（手動なら `python scripts/cachebust.py README.md README.en.md manga/README.md manga/README.en.md manga/README-LOG.md manga/README-LOG.en.md site/top.html site/top.en.html` のあと `python site/build.py`）。

リリースするときは、`lib/bootstrap.php` の `NM_VERSION` と `viewer/NagiManga.js` の `VERSION` を同じ番号に上げてから `dev/build_release.py` で ZIP を作り、GitHub のリリースに添付します。設置済みの NagiManga は、リリースの `nagimanga-vX.Y.Z.zip`（中身は `nagimanga/` フォルダ）を見て更新を知らせます。

番号の上げ方: 小さな修正や追加は最後の数字を上げます（0.5.0 → 0.5.1 → … → 0.5.99）。大きな更新のときだけ真ん中の数字を上げます（0.6.0）。設置済みの NagiManga は今より大きい番号のときだけ更新を知らせるので、一度出した番号より小さい番号は使いません。

## ライセンス

[MIT License](../LICENSE) (c) 2026 Lichiphen

QRコードは株式会社デンソーウェーブの登録商標です。
