@kubohiroya/turbowarp-extension-security-dialog 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE.md CHANGED
@@ -30,3 +30,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
30
  ## Public Editor screenshots
31
31
 
32
32
  `assets/screenshots/required-modes-{en,ja}-20261025.png` are unmodified browser captures of the public Editor on 25 October 2026, showing Camera Source 0.19.3, Asset Cache 0.3.1 and Bubble 0.13.4. Their origin, Editor revisions and hashes are recorded in `assets/screenshots/capture-20261025.json`. The captured UI, icons and banners retain their respective rights holders and applicable licenses; this package does not relicense those elements.
33
+
34
+ `assets/screenshots/localized-descriptions-{en,ja}-20261025.png` are unmodified captures of the updated public Editor on 25 October 2026, showing localized descriptions for Camera Source 0.19.4, Asset Cache 0.3.2 and Bubble 0.13.5. Their origin, revisions and hashes are recorded in `assets/screenshots/localized-descriptions-20261025.json`. The captured UI, icons and banners retain their respective rights holders and applicable licenses.
35
+
36
+ `assets/screenshots/required-approval-{en,ja}-20261028.png` are unmodified captures of the public Editor on 28 October 2026 after the required-mode approval update, showing Camera Source 0.19.4, Asset Cache 0.3.2 and Bubble 0.13.5 with Deny and With sandbox disabled. Their origin, verified distribution hashes, Editor revisions and image hashes are recorded in `assets/screenshots/required-approval-20261028.json`. The captured UI, icons and banners retain their respective rights holders and applicable licenses.
37
+
38
+ `assets/screenshots/preselected-required-{en,ja}-20261028.png` are unmodified captures of the public Editor on 28 October 2026 after automatic preselection was added when all listed extensions require unsandboxed execution. Camera Source 0.19.4, Asset Cache 0.3.2 and Bubble 0.13.5 have Without sandbox preselected and still require OK before execution. Origin, verified distribution hashes, Editor revisions and image hashes are recorded in `assets/screenshots/preselected-required-20261028.json`. The captured UI, icons and banners retain their respective rights holders and applicable licenses.
39
+
40
+ `assets/screenshots/hidden-bulk-required-{en,ja}-20261028.png` are unmodified captures of the public Editor on 28 October 2026 with Without sandbox preselected for all three required extensions and the redundant bulk selection section hidden. Camera Source 0.19.4, Asset Cache 0.3.2 and Bubble 0.13.5 still require OK before execution. Origin, verified distribution hashes, Editor revisions and image hashes are recorded in `assets/screenshots/hidden-bulk-required-20261028.json`. The captured UI, icons and banners retain their respective rights holders and applicable licenses.
package/README.ja.md CHANGED
@@ -1,21 +1,103 @@
1
1
  # turbowarp-extension-security-dialog
2
2
 
3
- [English](./README.md) | **日本語**
3
+ [English](README.md) | 日本語
4
4
 
5
5
  TurboWarpの複数の機能拡張を、1つの一覧で確認するための共通パッケージです。
6
6
  関連タスクと受け入れ基準は [scratch-gui #33](https://github.com/kubohiroya/scratch-gui/issues/33) に記録しています。
7
7
 
8
+ ## Pagesのカタログから機能拡張を選択する
9
+
10
+ `ExtensionSelectionList` は既存のバナー・メタデータ・詳細表示を共有し、各行の右端へチェックボックスを1つ表示します。
11
+ TurboWarp Plus一覧の対応範囲は [#28](https://github.com/kubohiroya/turbowarp-extension-security-dialog/issues/28) に記録しています。
12
+ ホスト側でGitHub PagesのJSONをHTTPS取得し、このコンポーネントへ渡します。
13
+
14
+ ```jsx
15
+ import {loadExtensionCatalog} from '@kubohiroya/turbowarp-extension-security-dialog';
16
+ import {ExtensionSelectionList} from '@kubohiroya/turbowarp-extension-security-dialog/react';
17
+
18
+ const controller = new AbortController();
19
+ const entries = await loadExtensionCatalog(
20
+ 'https://kubohiroya.github.io/turbowarp-plus-packages/extensions-v1.json',
21
+ controller.signal
22
+ );
23
+
24
+ const list = <ExtensionSelectionList entries={entries} locale="ja"
25
+ filterQuery={searchText} loadedIds={loadedExtensionIds} busy={loading}
26
+ onCancel={closeLibrary}
27
+ onComplete={ordered => host.reviewAndLoadInOrder(ordered)} />;
28
+ ```
29
+
30
+ 例の `host.reviewAndLoadInOrder` はホスト側で実装する実行許可確認・VM読み込み処理です。
31
+ `onComplete` は依存先を先に並べた重複のない配列を返し、`loadedIds` は除外します。
32
+ ある拡張を選ぶと推移的な依存先すべてがchecked・disabledになります。
33
+ 親を解除すると依存関係を再計算し、ほかの選択やユーザー自身の明示選択が必要としている拡張は選択を保ちます。
34
+ 読み込み済みの行はchecked・disabledです。検索と表示言語を切り替えても選択を保ちます。
35
+ `busy` で選択・フッター操作を無効にします。取得失敗・再試行・カタログの取得中止・VMエラーの表示と、
36
+ 読み込み成功に応じた `loadedIds` 更新はホスト側の担当です。
37
+
38
+ 各行は「手動で選択」(青)・「依存関係で自動選択」(紫)・「未選択」(中立色)を、背景・左枠・
39
+ アイコン・状態名で区別します。「読み込み済み」も独立した表示です。一覧の上部には凡例を表示します。
40
+ 自動選択した行には「Bubble、Sprite に必要」のように、必要としている明示選択元すべてを表示します。
41
+ 手動で選んだ拡張がほかの選択元の依存先になった場合も、「手動で選択」を維持し、依存元を併記して
42
+ チェックボックスを解除不可にします。DOMの `data-selection-state` は順に `selected` / `dependency` /
43
+ `unselected` / `loaded` です。
44
+
45
+ 任意のカテゴリ定義がある場合、ダッシュボードと同じ順序でカテゴリ見出し(`h2`)を表示します。
46
+ 日本語は `titleJa` があれば使用し、なければ `title` にフォールバックします。
47
+ 検索で空になったカテゴリは非表示になりますが、選択状態と「詳細」の対象は維持します。
48
+ カテゴリのない旧JSONは従来の平坦な一覧です。カテゴリ付きと未指定の項目が混在する場合、未指定分は
49
+ 末尾の「その他の機能拡張」へ表示します。配色はホストのモーダル背景色・文字色に従います。
50
+ 強調色は `--twextsec-manual-accent` / `--twextsec-dependency-accent` /
51
+ `--twextsec-unselected-accent` / `--twextsec-loaded-accent` で調整できます。
52
+
53
+ 一覧の表示・選択だけでは機能拡張のコードを読み込み・実行しません。
54
+ JSONのメタデータを直ちに表示し、「詳細」を開いたときだけソースを文字列として取得します。
55
+ 取得に失敗してもカタログのメタデータと選択は利用できます。
56
+ 実行許可確認には `ExtensionSecurityDialog context="catalog"` を使用でき、キャンセル文言が選択一覧へ戻る案内になります。
57
+ 既定の `context="project"` はプロジェクト読み込み用の文言を維持します。
58
+ ホストは確定された実行モードと独自のセキュリティ方針を尊重して読み込んでください。
59
+
60
+ JSON契約は `schemaVersion: 1` と `extensions` 配列です。各要素の必須項目は次のとおりです。
61
+
62
+ | 項目 | 内容 |
63
+ | --- | --- |
64
+ | `id` | ASCII英数字の機能拡張ID |
65
+ | `name` | 表示名 |
66
+ | `version` | 配布バージョン |
67
+ | `url` | バージョン固定の公開済み機能拡張JSへのHTTPS URL |
68
+ | `dependencies` | 同じJSONに含まれる依存先の機能拡張ID配列。npmパッケージ名ではない |
69
+
70
+ `package`・`description`・`descriptions`(`ja`等の小文字言語タグをキーとする説明)・`author`・`license`・
71
+ `banner`・`repository`・`unsandboxed`(例: `required`)は任意のメタデータです。
72
+ 完全なJSON例は[英語版のAPI例](README.md#selecting-extensions-from-a-pages-catalog)を参照してください。
73
+ 生成側でパッケージ依存を拡張IDへ変換し、ビルド専用パッケージを除外します。
74
+
75
+ カテゴリはトップレベルの任意項目 `categories: [{id, title, titleJa?}]` としてダッシュボード順に定義し、
76
+ 各 `extensions[].category` にそのIDを指定します。カテゴリIDの重複・不正な定義・未定義IDの参照は拒否します。
77
+ `parseExtensionCatalog` の返り値は従来どおり配列で、カテゴリ参照は各要素の
78
+ `category: {id, title, titleJa?, order}` へ解決します。`order` は定義の配列順です。
79
+
80
+ `parseExtensionCatalog` は未対応schema、ID/URL重複、存在しない依存先、循環依存、HTTPS以外や認証情報付きのURLを拒否します。
81
+ `resolveExtensionSelection(entries, explicitIds, loadedIds)` はReactなしでも同じ選択集合・依存順を計算します。
82
+ 返り値の `requiredBy` は依存先IDをキー、必要としている明示選択元IDの集合を値とするMapです。
83
+ 推移的依存・共有依存を含め、選択を解除すると依存元の集合も再計算します。
84
+ `loadExtensionCatalog` は任意のAbortSignalを受け付け、認証情報・Refererを送らずに取得し、HTTP失敗や不正なJSON契約を例外として返します。
85
+
86
+ 切り戻しはホストの起動時固定・既定OFFのフラグをOFFにして再読み込みするか、カタログ接続差分をrevertします。
87
+ 既存の3つの実行モードを持つセキュリティ確認は、そのまま利用できます。
88
+
8
89
  ## 公開Editorのスクリーンショット
9
90
 
10
- [英語版](./README.md)と日本語版は、同じダイアログの表示言語を切り替えています。
11
- 2026年10月25日の[公開Editor](https://kubohiroya.github.io/turbowarp-extension-data-editor/editor.html?lang=ja)で、
12
- Camera Source 0.19.3・Asset Cache 0.3.1・Bubble 0.13.4を開いた実画面です。
91
+ [英語版](./README.md)と日本語版は、同じダイアログの表示言語を切り替え、ラベルと拡張の説明文を各言語で表示しています。
92
+ 全件必須時の自動選択と一括選択欄の非表示に対応した[公開Editor](https://kubohiroya.github.io/turbowarp-extension-data-editor/editor.html?lang=ja)で、
93
+ 2026年10月28日にCamera Source 0.19.4・Asset Cache 0.3.2・Bubble 0.13.5を開いた実画面です。
13
94
 
14
- ![3拡張の日本語確認画面。サンドボックスありは非対応で無効、サンドボックスなしは動作に必要。サンドボックス内では動作しない理由を表示](https://raw.githubusercontent.com/kubohiroya/turbowarp-extension-security-dialog/main/assets/screenshots/required-modes-ja-20261025.png)
95
+ ![Camera Source・Asset Cache・Bubbleの説明が日本語で表示された修正後の確認画面。拒否とサンドボックスありは無効、サンドボックスなしを選択済みで動作に必要と表示。一括選択欄は非表示。OKで読み込むかキャンセルで中止できる](https://raw.githubusercontent.com/kubohiroya/turbowarp-extension-security-dialog/main/assets/screenshots/hidden-bulk-required-ja-20261028.png)
15
96
 
16
- 3拡張とも`Unsandboxed: required`を宣言しています。「サンドボックスあり」は無効化され、
17
- 「この拡張はサンドボックス内では動作しません。」と理由が表示されます。
18
- 「サンドボックスなし」には「動作に必要」と表示します。拒否は選択でき、実行には自分での選択とOKが必要です。
97
+ 3拡張とも`Unsandboxed: required`を宣言しています。「拒否」と「サンドボックスあり」は無効化され、
98
+ 理由が表示されます。「サンドボックスなし」だけが有効で、「動作に必要」と表示します。
99
+ 全拡張がこのモードを必要とする場合、全件のメタデータ取得後に選択済みになり、一括選択欄は非表示になります。
100
+ 「OK」で続行するか、「キャンセル」でプロジェクトの読み込みを中止します。
19
101
  必要なモードの表示は、安全性や配布元の信頼性の認定ではありません。
20
102
 
21
103
  | 機能拡張 | サンドボックスなしが必要な理由 |
@@ -25,7 +107,7 @@ Camera Source 0.19.3・Asset Cache 0.3.1・Bubble 0.13.4を開いた実画面で
25
107
  | Bubble | ホストの実行環境と描画機能へ直接アクセスして、ステージ上に重ねた吹き出しや立ち絵を描画・更新します。 |
26
108
 
27
109
  画像はダイアログ部分をブラウザーで直接撮影したもので、加工していません。
28
- 取得元の版・Editor build・画像hashは[撮影台帳](./assets/screenshots/capture-20261025.json)に記録しています。
110
+ 取得元の版・Editor build・画像hashは[撮影台帳](./assets/screenshots/hidden-bulk-required-20261028.json)に記録しています。
29
111
 
30
112
  ## 連動するパッケージ
31
113
 
@@ -186,17 +268,36 @@ HTTP/HTTPSの出所URLは完全なURLを表示し、別タブで開きます。
186
268
  プレビュー枠内をスクロールして確認できます。ポインターを外すかEscapeで閉じ、選択や詳細状態を保持します。
187
269
  メタデータの非同期取得中も実行方法を選べます。ソースの一覧内表示は引き続き「詳細」を押したときだけです。
188
270
 
271
+ ### 機能拡張の説明文の言語
272
+
273
+ 拡張コードを実行せず、任意の言語別ヘッダーコメントを読み取ります。
274
+
275
+ ```js
276
+ // Description: Share one camera stream between extensions.
277
+ // Description[ja]: 1つのカメラ映像を複数の機能拡張で共有します。
278
+ // Description[ja-JP]: 日本語の説明(日本向け)。
279
+ ```
280
+
281
+ 言語タグの大文字・小文字は区別しません。Editorの言語の完全一致、基底言語、従来の`Description`の順で説明文を選びます。表示中に言語を変えると、ソースを再取得せず説明が切り替わり、許可の選択状態は保たれます。翻訳がない既存の拡張も従来どおり表示できます。
282
+
283
+ 同梱メンバーのnoticeにある説明は親バンドルへ混ぜません。HTMLに見える説明も文字列として表示します。独自の`loadInfo`を使う場合は、任意の`ExtensionMetadata.descriptions`へ小文字の言語タグをキーとした説明を渡せます。`getExtensionDescription(metadata, locale)`でも同じ選択処理を利用できます。
284
+
285
+ 上の画像は、日本語説明へ対応した公開Editorと公開済みの3拡張を使用しています。英語版では各拡張の従来の`Description`、日本語版では`Description[ja]`を表示します。対応する翻訳がない拡張は元の説明を表示します。説明の言語が変わっても、必要な実行モードや許可の選択は変わりません。パッケージを更新しても、既存SB3に埋め込まれたコードは自動で差し替わりません。
286
+
189
287
  ## 実行モードの選択
190
288
 
191
289
  個別選択・一括選択・一覧の見出しは、禁止アイコンの「拒否」、盾の「サンドボックスあり」、
192
290
  警告の「サンドボックスなし」の順です。アイコンと色は文字ラベルを補助し、安全性の認定には使いません。
193
291
 
194
- 先頭の宣言コメントに `// Unsandboxed: required` があれば、「サンドボックスあり」を無効化して
195
- 動作しない理由を表示し、「サンドボックスなし」に「動作に必要」を付けます。拒否は選択できます。
292
+ 先頭の宣言コメントに `// Unsandboxed: required` があれば、「拒否」と「サンドボックスあり」を無効化して
293
+ 理由を表示し、選択できる唯一のモード「サンドボックスなし」に「動作に必要」を付けます。
294
+ 選択したモードを「OK」で承認するか、「キャンセル」でプロジェクトの読み込みを中止します。
196
295
  この宣言は作者の自己申告であり、未宣言・取得失敗から必須モードを推測しません。
197
296
 
198
- 実行する選択の確定はメタデータ取得の完了または失敗後です。遅れて取得した宣言に対応しない選択は、
199
- 保存済みの復元値も含めて解除し、選び直しを案内します。権限の広いモードへ自動で変更しません。
297
+ 「拒否」を含む選択の確定はメタデータ取得の完了または失敗後です。全拡張の取得が成功し、すべてが
298
+ この必須モードを宣言していれば、非対応の保存済み選択も含めて「サンドボックスなし」を選択済みにします。
299
+ 「OK」を押すまで拡張は実行せず、「キャンセル」で中止できます。取得中・未宣言・任意指定・取得失敗が
300
+ 1件でもあれば、この一括初期選択は行いません。混在する一覧では、非対応の選択を解除して選び直しを案内します。
200
301
  一括選択は対応する拡張だけに適用し、全件が非対応の一括ボタンは無効化します。
201
302
 
202
303
  ## 接続例
@@ -238,8 +339,8 @@ scratch-guiは既定OFFの起動時フラグで導入し、ホスト独自のセ
238
339
  ## 開発と検証
239
340
 
240
341
  ```sh
241
- npm install
242
- npm test
342
+ pnpm install
343
+ pnpm test
243
344
  ```
244
345
 
245
346
  GUIとの統合では、実VMでの拒否後の表示・保存、エディター初期画面への遷移、
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # turbowarp-extension-security-dialog
2
2
 
3
- **English** | [日本語](./README.ja.md)
3
+ English | [日本語](README.ja.md)
4
4
 
5
5
  A shared package that reviews every custom extension a TurboWarp project is about to load
6
6
  in **one single list**, instead of one prompt per extension.
@@ -9,17 +9,18 @@ Related tasks and acceptance criteria are tracked in
9
9
 
10
10
  ## Screenshots from the public Editor
11
11
 
12
- [English](./README.md) and [Japanese](./README.ja.md) use the same dialog with localized labels.
13
- This capture shows Camera Source 0.19.3, Asset Cache 0.3.1 and Bubble 0.13.4 in the
12
+ [English](./README.md) and [Japanese](./README.ja.md) use the same dialog with localized labels and extension descriptions.
13
+ This capture shows Camera Source 0.19.4, Asset Cache 0.3.2 and Bubble 0.13.5 in the
14
14
  [public Editor](https://kubohiroya.github.io/turbowarp-extension-data-editor/editor.html?lang=en)
15
- on 25 October 2026.
15
+ on 28 October 2026, with **Without sandbox** already selected for all three extensions and the unnecessary bulk selection section hidden.
16
16
 
17
- ![English extension review: With sandbox is disabled as Unsupported for all three extensions; Without sandbox is marked Required to run, with the reason This extension cannot run inside the sandbox.](https://raw.githubusercontent.com/kubohiroya/turbowarp-extension-security-dialog/main/assets/screenshots/required-modes-en-20261025.png)
17
+ ![English descriptions for Camera Source, Asset Cache and Bubble: Deny and With sandbox are disabled for all three extensions; Without sandbox is preselected and marked Required to run. The bulk selection section is hidden. Choose OK to load the project or Cancel to stop.](https://raw.githubusercontent.com/kubohiroya/turbowarp-extension-security-dialog/main/assets/screenshots/hidden-bulk-required-en-20261028.png)
18
18
 
19
- All three extensions declare `Unsandboxed: required`. **With sandbox** is disabled, with
20
- **This extension cannot run inside the sandbox.** shown as the reason. **Without sandbox**
21
- is marked **Required to run**. Deny remains available, and running still requires an explicit
22
- choice and OK. These labels describe execution requirements, not a safety certification.
19
+ All three extensions declare `Unsandboxed: required`. **Deny** and **With sandbox** are
20
+ disabled, with the reason shown. **Without sandbox** is enabled and marked **Required to run**.
21
+ When every listed extension requires this mode, it is preselected after all metadata loads, and the bulk selection section is hidden.
22
+ Press OK to continue, or **Cancel** to stop loading the project.
23
+ These labels describe execution requirements, not a safety certification.
23
24
 
24
25
  | Extension | Why it needs to run without a sandbox |
25
26
  | --- | --- |
@@ -28,7 +29,7 @@ choice and OK. These labels describe execution requirements, not a safety certif
28
29
  | Bubble | Accesses the host runtime and renderer directly to draw and update layered speech bubbles and portraits on the stage. |
29
30
 
30
31
  The PNG is an unmodified, modal-only browser capture. Source versions, Editor build and image
31
- hashes are recorded in [the capture manifest](./assets/screenshots/capture-20261025.json).
32
+ hashes are recorded in [the capture manifest](./assets/screenshots/hidden-bulk-required-20261028.json).
32
33
 
33
34
  ## How it fits together
34
35
 
@@ -193,21 +194,48 @@ or a file timestamp.
193
194
 
194
195
  Run modes remain selectable while metadata is still loading.
195
196
 
197
+ ### Localized extension descriptions
198
+
199
+ The dialog reads optional language-tagged header comments, without evaluating extension code:
200
+
201
+ ```js
202
+ // Description: Share one camera stream between extensions.
203
+ // Description[ja]: 1つのカメラ映像を複数の機能拡張で共有します。
204
+ // Description[ja-JP]: 日本語の説明(日本向け)。
205
+ ```
206
+
207
+ Tags are case-insensitive. Display selection uses the exact Editor locale, then its base
208
+ language, then the original `Description`. A locale change updates the text without refetching
209
+ source or changing permission choices. Missing translations keep existing extensions compatible.
210
+ Only the enclosing header is used; bundled member notices cannot supply its translation.
211
+ HTML-looking text is rendered as text. The optional `ExtensionMetadata.descriptions` record uses
212
+ lowercase keys, and `getExtensionDescription(metadata, locale)` exposes the same lookup.
213
+
214
+ The public Editor and the three published bundles shown above support localized descriptions.
215
+ The English capture uses each original `Description`; the Japanese capture uses `Description[ja]`.
216
+ Extensions without a matching translation retain their original description. The selected language
217
+ does not change required modes or permission choices. Updating a package does not replace code
218
+ already embedded in an existing SB3.
219
+
196
220
  ## Choosing a run mode
197
221
 
198
222
  Per-row choices, the bulk choices and the list heading are ordered: **Deny** (prohibition
199
223
  icon), **With sandbox** (shield), **Without sandbox** (warning). Icons and colour support the
200
224
  text labels; they are not a safety certification.
201
225
 
202
- If the leading declaration comment contains `// Unsandboxed: required`, *With sandbox* is
203
- disabled with the reason shown, and *Without sandbox* is marked *Required to run*. Deny stays
204
- selectable. This declaration is the author's own claim; a required mode is never inferred from
226
+ If the leading declaration comment contains `// Unsandboxed: required`, *Deny* and *With sandbox*
227
+ are disabled with the reason shown, and *Without sandbox* is the only available mode, marked
228
+ *Required to run*. Press OK to approve the selected modes, or Cancel to stop loading
229
+ the project. This declaration is the author's own claim; a required mode is never inferred from
205
230
  a missing declaration or a failed fetch.
206
231
 
207
- A choice to run is only committed once metadata has loaded or failed. A selection that
208
- contradicts a late-arriving declaration is cleared — including a restored saved value — and
209
- the user is asked to choose again. The mode is never silently widened. A bulk choice applies
210
- only to extensions that support it, and a bulk button is disabled when no listed extension does.
232
+ A choice, including Deny, is only committed once metadata has loaded or failed. When every
233
+ listed extension has loaded metadata declaring this requirement, *Without sandbox* is
234
+ preselected for all entries, including entries with incompatible saved choices. Nothing runs
235
+ until the user presses OK; Cancel still stops loading the project. Pending, missing, optional
236
+ or failed declarations prevent this batch default. In mixed batches, incompatible choices
237
+ are cleared and the user is asked to choose again. A bulk choice applies only to extensions
238
+ that support it, and a bulk button is disabled when no listed extension does.
211
239
 
212
240
  ## Wiring it up
213
241
 
@@ -228,6 +256,107 @@ surfacing as a VM error is identified with `isExtensionSecurityCancelled(error)`
228
256
  Under Webpack 4 with CSS Modules, load `webpack4.css` instead of `styles.css`; root-level
229
257
  compatibility entries resolve for older Webpack 4 setups that do not read package exports.
230
258
 
259
+ ## Selecting extensions from a Pages catalog
260
+
261
+ `ExtensionSelectionList` shares the security dialog's banners, metadata and Details view,
262
+ with one checkbox at the right of each row. This API supports the TurboWarp Plus library
263
+ tracked in [#28](https://github.com/kubohiroya/turbowarp-extension-security-dialog/issues/28).
264
+ The host fetches a versioned JSON catalog over HTTPS:
265
+
266
+ ```jsx
267
+ import {loadExtensionCatalog} from '@kubohiroya/turbowarp-extension-security-dialog';
268
+ import {ExtensionSelectionList} from '@kubohiroya/turbowarp-extension-security-dialog/react';
269
+
270
+ const controller = new AbortController();
271
+ const entries = await loadExtensionCatalog(
272
+ 'https://kubohiroya.github.io/turbowarp-plus-packages/extensions-v1.json',
273
+ controller.signal
274
+ );
275
+
276
+ const list = <ExtensionSelectionList entries={entries} locale="en"
277
+ filterQuery={searchText} loadedIds={loadedExtensionIds} busy={loading}
278
+ onCancel={closeLibrary}
279
+ onComplete={ordered => host.reviewAndLoadInOrder(ordered)} />;
280
+ ```
281
+
282
+ `host.reviewAndLoadInOrder` represents the host's permission review and VM loading path.
283
+ The callback receives dependencies before their dependants, each once, excluding
284
+ `loadedIds`. Selecting an extension checks and disables all transitive dependencies.
285
+ Removing its selection recomputes the closure; dependencies remain checked if another
286
+ selection needs them or the user selected them explicitly. Loaded rows are checked and
287
+ disabled. Search and locale changes preserve selection. `busy` disables selection and the
288
+ footer controls. The host owns fetch errors, retry, catalog cancellation, VM errors, and
289
+ updating `loadedIds` after each successful load.
290
+
291
+ Rows distinguish **Selected by you** (blue), **Selected automatically** (purple), and
292
+ **Not selected** (neutral), using a background, left border, icon and label together.
293
+ Already loaded rows have their own label and accent. A legend explains the states above
294
+ the list. Automatically selected dependencies name every selected root that needs them,
295
+ for example “Required by Bubble, Sprite”. An explicitly selected dependency keeps its
296
+ “Selected by you” state while another root requires it; its checkbox stays disabled and
297
+ the dependency reason is also shown. The row's `data-selection-state` is `selected`,
298
+ `dependency`, `unselected` or `loaded`.
299
+
300
+ Optional catalog categories render as semantic `h2` sections in dashboard order. Japanese
301
+ uses `titleJa` when available, otherwise `title`; other locales use `title`. Search hides
302
+ empty sections without changing selection or source controls. Catalogs without categories
303
+ keep a flat list. If categorized and uncategorized entries are mixed, the latter appear
304
+ under “Other extensions” at the end. Theme colors use the host's modal background and
305
+ foreground; accent overrides are `--twextsec-manual-accent`, `--twextsec-dependency-accent`,
306
+ `--twextsec-unselected-accent`, and `--twextsec-loaded-accent`.
307
+
308
+ The selection list never loads or runs extension code. Catalog metadata renders immediately;
309
+ only Details fetches the source as text. Source failures leave catalog metadata and selection
310
+ usable. `ExtensionSecurityDialog context="catalog"` provides permission review with cancel
311
+ wording that returns to this list; its default `context="project"` retains project wording.
312
+ The host must respect the committed execution modes and its own security policy.
313
+
314
+ The Pages document has the following contract (`schemaVersion: 1`):
315
+
316
+ ```json
317
+ {
318
+ "schemaVersion": 1,
319
+ "categories": [{"id": "camera", "title": "Camera", "titleJa": "カメラ"}],
320
+ "extensions": [
321
+ {
322
+ "id": "cameraSource",
323
+ "name": "Camera Source",
324
+ "category": "camera",
325
+ "version": "1.2.3",
326
+ "package": "@example/camera-source",
327
+ "url": "https://cdn.jsdelivr.net/npm/@example/camera-source@1.2.3/dist/extension.js",
328
+ "description": "Share a camera stream.",
329
+ "descriptions": {"ja": "カメラ映像を共有します。"},
330
+ "repository": "https://github.com/example/camera-source",
331
+ "banner": "https://example.com/camera-source.svg",
332
+ "unsandboxed": "required",
333
+ "dependencies": []
334
+ }
335
+ ]
336
+ }
337
+ ```
338
+
339
+ IDs contain ASCII letters and digits. `name`, `version`, `url`, and `dependencies` are
340
+ required. Dependencies are extension IDs in this document, not npm package names;
341
+ the generator resolves package dependencies to these IDs. `package`, descriptions, author,
342
+ license, banner, repository and execution requirement are optional metadata. The generator
343
+ uses version-pinned, published distribution URLs and excludes build-only packages.
344
+ `parseExtensionCatalog` rejects unsupported schemas, duplicate IDs/URLs, missing dependencies,
345
+ cycles, and non-HTTPS or credential-bearing URLs before the UI or VM receives entries.
346
+ `resolveExtensionSelection(entries, explicitIds, loadedIds)` exposes the same closure and
347
+ load order independently of React. Its `requiredBy` map associates each dependency ID with
348
+ a set of the explicit root IDs that require it, including transitive and shared dependencies.
349
+ Optional `categories: [{id, title, titleJa?}]` are declared once, in dashboard order;
350
+ `extensions[].category` references one of those IDs. Duplicate category IDs, invalid
351
+ definitions and unknown references are rejected. `parseExtensionCatalog` retains its array
352
+ return type and resolves each reference to `entry.category: {id, title, titleJa?, order}`.
353
+ `loadExtensionCatalog` accepts an optional AbortSignal,
354
+ omits credentials/referrers, and rejects failed HTTP responses or invalid JSON contracts.
355
+
356
+ To roll back catalog integration, turn off the host's startup flag (off by default) and
357
+ reload, or revert the catalog wiring. The existing three-way security dialog remains
358
+ available without the catalog API.
359
+
231
360
  ## Stored preferences
232
361
 
233
362
  Preferences are saved only when OK was pressed **and** the project then opened successfully.
@@ -252,8 +381,8 @@ own security policy take precedence.
252
381
  ## Development and verification
253
382
 
254
383
  ```sh
255
- npm install
256
- npm test
384
+ pnpm install
385
+ pnpm test
257
386
  ```
258
387
 
259
388
  GUI integration additionally verifies display and storage after a real-VM denial, the