Scratch 3 の公式エディターには、利用者が機能を足すための仕組みがありません。そこを埋めたのが、有志のブラウザー拡張 Scratch Addons です。scratch.mit.edu のページにスクリプトを注入し、画面の要素や内部の状態を外から探して機能を付け足します。
TurboWarp は、この Scratch Addons のうちエディター向けのものを取り込み、自分のビルドに同梱しました。書き方も API もほぼ同じですが、ブラウザー拡張ではなくエディターの一部として動きます。
TurboWarp-Plus-Editor はこの向きを逆にしました。フォークした scratch-gui に「ここで何か渡されていれば使う」という名前付きの口を置き、ビルド時に Plus 側の実装をそこへ結び付けます。何も渡されなければ、フォークは本家の TurboWarp と同じに動きます。
図1アドオンは機能の側からエディターへ手を伸ばします。Plus ではエディターの側が、口ごとに Plus の実装を呼びます。
Scratch Addons
ブラウザーに入れて使うブラウザー拡張です。Scratch 公式サイトのエディターだけでなく、プロフィールやフォーラムのページにも作用します。
- 入れ方
- 利用者がブラウザーに導入
- 切り替え
- ブラウザー拡張の設定画面で個別に
- 触り方
- DOM・Redux・VM を外から
TurboWarp のアドオン
Scratch Addons のエディター向けを同梱したものです。API は TurboWarp が作り直していて、独自の tw- アドオンも 6 件あります。
- 入れ方
- 最初から同梱
- 切り替え
- /addons の設定画面
- 触り方
- 上と同じ API で外から
TurboWarp-Plus-Editor
フォークの名前付きの口に、ビルド時に実装を渡します。実装の本体はバージョン付きの npm パッケージにあります。
- 入れ方
- ビルドに組み込む
- 切り替え
- 起動時の URL フラグ
- 触り方
- GUI から呼ばれる
アドオンの方式は、エディター本体に手を入れずに済みます。そのかわり、画面の構造が変わると黙って効かなくなります。Plus の方式はフォークに呼び出しの行が残りますが、契約が型とテストで固定され、作品の読み込みより前に効かせる処理やレイアウト全体の置き換えまで扱えます。
Plus の主な機能はレイアウト、拡張の審査、保存の流れです。これらは「外から付け足す」モデルに収まらないので、Plus がアドオン互換を目指す意味は大きくありません。外して戻せる小さな機能については、アドオンと同じく後から付けて外せる形で書くことにしています。
詳しい比較は 02 比較と結論、口の一覧は 03 仕様書 にあります。
1. Scratch Addons:ページに注入されるブラウザー拡張
Scratch Addons は Chrome や Firefox に入れるブラウザー拡張です。アドオンは1つずつマニフェストを持ち、名前・説明・タグ・設定項目・既定で有効かどうか・どのページで動くかを宣言します。条件に合うページを開くと、ブラウザー拡張が userscript と userstyle をそのページに注入します。
userscript は ({addon, console, msg}) を受け取る非同期関数です。addon.tab の waitForElement で画面要素の出現を待ち、traps で VM や Blockly を手に入れ、Redux の状態を見張ります。公式エディターには口がないので、すべてを外から探し当てます。
図2Scratch Addons では、公式エディターは何も知りません。userscript が画面の要素と内部状態を外から見つけて付け足します。
2. TurboWarp のアドオン:同梱され、同じ API で動く
TurboWarp は Scratch Addons のエディター向けアドオンを、pull.js というスクリプトで自分のソースに取り込みます。上流のコードに TurboWarp 用のパッチ(TurboWarp/addons の tw ブランチ)を当て、さらに正規表現で書き換えてから src/addons/ に置きます。今のフォークには 81 件が入っていて、そのうち 6 件は TurboWarp 独自の tw- アドオンです。
実行時は、api.js が Scratch Addons の addon オブジェクトを作り直して渡します。ログイン情報のようにブラウザー拡張にしかない API はありません。そのかわり、appendToSharedSpace の共有スペース(stageHeader、fullscreenStageHeader、afterGreenFlag、afterStopButton、afterSoundTab、monitor、paintEditorZoomControls と、アセットの右クリックメニューの2か所、合わせて 9 か所)のように、TurboWarp の画面に合わせた置き場所が用意されています。メニューバーに「録画」ボタンを足す mediarecorder も、この仕組みでファイルメニューの横の要素を探して差し込みます。
図3TurboWarp では、取り込みはビルド時、接続は実行時です。接続のしかたは Scratch Addons と同じく外から探す形です。
3. TurboWarp-Plus-Editor:ビルド時に口へ結び付ける
Plus はフォークした scratch-gui に、本家に無いファイルを1つも置きません。本家にもある src/addons/hooks.js に getHostIntegration() を置き、hooks.js 以外の 28 のファイルが getHostIntegration()?.EditorLayout のように名前を指定して問い合わせます。
配信用のビルドでは、webpack の ProvidePlugin が __TW_GUI_HOST__ を本リポジトリの site/gui-integration/index.ts に結び付けます。そこから site/plus-integration.ts の統合オブジェクトと、npm で版を持つ @kubohiroya/turbowarp-* パッケージへつながります。GUI を単体でビルドすると __TW_GUI_HOST__ は存在せず、getter は null を返し、本家どおりに動きます。
図4Plus ではフォークの呼び出し箇所が口の名前を指定し、ビルド時に供給された値を受け取ります。値が無いときは本家と同じ動きに落ちます。
4. 効き始める時期
三つの方式は、機能がいつ効き始めるかが違います。アドオンはページや GUI が動き出してから非同期に付くので、作品の読み込みより前に必ず効いていなければならない処理には向きません。Plus は、拡張の審査のように読み込みの前に決まっていなければならない処理を、口として同期的に渡せます。
図5アドオンは後から付き、利用者が切り替えられます。Plus は最初から結び付いていて、作品の読み込みより前に効かせる処理も持てます。
5. 本家が変わったときの壊れ方
アドオンは CSS モジュールのクラス名を前方一致で探します(scratchClass、[class*='menu-bar_file-group'] など)。本家が画面の構造を変えると、探し当てられずに何も起きません。Plus の口はフォークのソースに書かれた呼び出しなので、本家の変更とぶつかればマージの衝突や型検査、統合テストで止まります。
図6アドオンは黙って効かなくなります。Plus はフォークを更新する時点で気付けます。
6. 観点ごとの比較
| 観点 | Scratch Addons | TurboWarp のアドオン | TurboWarp-Plus-Editor |
| 配布の形 | ブラウザー拡張 | エディターのビルドに同梱 | npm パッケージをビルドに組み込む |
| 機能の単位 | マニフェスト+userscript/userstyle | 同じ(_manifest_entry.js に変換) | パッケージの関数・コンポーネントと、統合オブジェクトの項目 |
| エディターの改変 | 不要 | 実行器だけ(src/addons/) | フォーク 28 ファイルに呼び出し行 |
| 接続の時期 | 実行時(注入) | 実行時(GUI の起動後) | ビルド時に結合、描画時に同期参照 |
| 接続点 | DOM・Redux・VM・Blockly を外から | 同じ+共有スペース 9 か所 | 名前付きの口(React コンポーネント、reducer の断片、サービス) |
| できること | 付け足し、部分的な差し替え | 同じ | 付け足しに加え、レイアウト全体の置き換え、読み込み前の審査、保存の流れ |
| 切り替え | 利用者が設定画面で | 利用者が /addons で | 起動時の URL フラグ(?twPlus…=1) |
| 本家が変わったとき | 黙って効かなくなる | 同じ | マージ・型検査・テストで止まる |
| 契約の明示 | 開発者ドキュメント | 文書なし(実装が仕様) | 型付きの口と単体テスト |
| 他の作品・プレイヤーとの共有 | なし | なし | 同じパッケージを Packager の単一 HTML でも使う |
7. Plus の追加機能をアドオンの形に寄せられるか
Plus の追加機能を「付け足すか、置き換えるか」と「利用者が切ってよいか、常に効いている必要があるか」の二つの軸で並べると、アドオンの形に寄せられるのは左上の区画だけです。
図7アドオンの形に寄せられる A は付け足しの小さな機能だけです。Plus の中心にある C は、アドオンのモデルの外側にあります。
8. 結論:Plus の方式が優れている理由
TurboWarp-Plus-Editor が TurboWarp に機能を足すには、アドオンの方式よりも、フォークに名前付きの口を置いてビルド時に実装を結び付ける今の方式が適しています。理由は次の6つです。
- 作品より先に効かせられる拡張の審査や信頼する拡張の一覧は、作品の読み込みより前に決まっていなければなりません。起動後に非同期で付くアドオンでは、この順序を保証できません。口は描画の時点で同期的に参照されます。
- 構造ごと置き換えられるDockview のレイアウト、ステージパネル、スプライト一覧、保存の流れは、既存の画面に付け足すものではありません。アドオンで実現すると、本家の DOM を隠して作り直すことになります。口なら React コンポーネントや reducer の断片をそのまま渡せます。
- 契約が見え、テストできるアドオンの接続点はハッシュ付きのクラス名や画面の文言です。Plus の口は名前と引数が決まっていて、
test/plus-integration.test.ts や test:gui-integration で固定できます。本家が変わったときも、黙って壊れずにテストで止まります。
- 本家との差分を小さく保てる口はすべて任意で、何も渡さなければ本家と同じに動きます。フォークに残るのは呼び出しの行だけで、本家に無いファイルは 0 件です。本家の更新を取り込むときの衝突が、呼び出しの行に限られます。
- 実装を版付きの部品として再利用できる実装の本体は
@kubohiroya/turbowarp-* の npm パッケージにあり、それぞれが版・テスト・リリースを持ちます。同じパッケージを Packager が作る単一 HTML のプレイヤーでも使えます。アドオンはそのエディターの中でしか動きません。
- 実行時の拡張ともつながる口の一部は
Scratch.gui.stageControls や Scratch.gui.blockEditors として拡張に公開され、カメラなどの拡張が Editor の操作欄に自分の操作を登録できます。アドオンの API は拡張から使う前提になっていません。
引き受けているコスト
利用者が機能を個別に切り替える画面はありません。フォークの呼び出し行を本家の更新のたびに保守する必要があり、機能を足すにはビルドし直す必要があります。第三者が実行時に機能を足すこともできません。ただし、エディター本体ではない機能は、実行時の拡張として操作欄の API から足せます。
そのうえで、図7の A に当たる小さな機能は、アドオンと同じく「後から付けて、dispose で外せる」形で書きます。こうしておけば、利用者が切り替えられるようにしたくなったとき、アドオンの形へ移しやすくなります。既存の機能をアドオン互換に作り直す作業は行いません。
根拠:フォーク src/addons/(81 件、うち tw- 6 件)、api.js の appendToSharedSpace、docs/plus-integration-sockets.md、docs/gui-integration/README.md、site/plus-integration.ts。件数は 2026年11月14日に、このリポジトリが固定しているフォーク(site/source.json の guiRevision、f1e8f96)で数えたものです。フォークを更新したら数え直します。
1. 3つのリポジトリと依存の向き
Plus の追加機能は3つのリポジトリにまたがります。依存は一方向で、上流は下流を知りません。
図8実装はパッケージに、接続と配信は Editor に、口を呼ぶ行だけがフォークにあります。パッケージ側は GPL のコードを import しないので、向きは「GPL 側がパッケージの口を呼ぶ」に固定します。
2. ビルド時の結合
フォークの src/addons/hooks.js は次の getter を持ちます。本家にも存在するファイルです。
/* global __TW_GUI_HOST__ */
// The distributing build supplies this module; standalone GUI builds have no extra host.
export const getHostIntegration = () =>
(typeof __TW_GUI_HOST__ === 'undefined' ? null : __TW_GUI_HOST__);
配信ビルドの scripts/site/webpack.config.cts が ProvidePlugin で __TW_GUI_HOST__ を site/gui-integration/index.ts に結び付けます。実行時の global への代入や起動順には依存しません。フォークが読む値は2層になっています。
図9画面の部品は橋渡しから直接読み、設定値やサービスは補助関数を通して統合オブジェクトから読みます。補助関数が「無いときの既定値」を一か所で決めます。
3. 口の一覧
フォークが参照している口を用途ごとに並べます。左の名前はフォークが getHostIntegration()?. の後に書く名前、右の項目は統合オブジェクトの項目です。menu-bar.jsx と extension-library.jsx は getHostIntegration() をいったん変数 host に受けてから読むので、最新の一覧はフォークの checkout で次の2つのコマンドを合わせて取り出します。hostIntegration は口ではなく、host… 補助関数に統合オブジェクトを渡すための getter です。
表は 2026年11月14日に、固定しているフォーク(f1e8f96)で取り出したものです。
grep -rhoE "getHostIntegration\(\)\?\.[A-Za-z]+" src | sort | uniq -c
grep -hoE "host\??\.[A-Za-z]+" src/components/menu-bar/menu-bar.jsx src/containers/extension-library.jsx | sort -u
| 用途 | フォークが読む名前 | 統合オブジェクトの項目 | 主な呼び出し元 |
| 画面の構造 | EditorLayout StagePanel StageDisplayMenu TargetListLayout TargetListPanel SpriteGrid BackdropTile BackdropListActions BackpackSurface | (直接の値) | gui.jsx ほか |
| スプライトとステージ | wrapSpriteInfo spriteListClasses useSpriteCodeWindows attachSpriteCodeWorkspace attachStageInput attachStageMonitorDrag | (直接の値) | sprite-info.jsx、stage.jsx ほか |
| メニュー | renderFileMenu createFileMenuItem extensionInFileMenu renderProjectSearch ProjectInfoMenu renderStageControls | (直接の値) | menu-bar.jsx、controls.jsx |
| ワークスペースとブロック | createWorkspaceAttachment registerHostBlockShapes createBlocksWrapper getBlockEditorService attachHostScriptTransfer | attachToWorkspace registerBlockShapes blockEditor attachWorkspaceMinimap | gui.jsx、blocks.js、blocks.jsx |
| 状態 | hostModalNames hostModalDefaults createCancelSecurityLoad recoverProjectLoad loadedProjectTitle restoreProjectTitle | modals | reducers/、vm-manager-hoc.jsx |
| 拡張の読み込みと審査 | installSecurityHost getPlusReviewedMode createPreviewVm hostLibraryTab | installExtensionHost securityReview trustedExtensions libraryTab extensionLoadErrors | tw-security-manager.jsx、extension-library.jsx |
| 入力と翻訳 | hostIgnoresKeyEvent hostTranslationsFor | shouldIgnoreKeyEvent translations | vm-listener-hoc.jsx、tw-translations/ |
| アドオンとの接点 | hostAwaitedAddonIds finalizeHostTransferMenu hostStageTransferMenu | awaitStartupAddons scriptTransfer | addons/api.js |
| Packager | hostProjectPackager | projectPackager | tw-packager-integration-hoc.jsx |
| 拡張に公開する API | hostStageControls getBlockEditorService createPreviewVm | stageControls blockEditor | tw-extension-gui-api.js |
4. 口の約束
すべての口は次の約束を守ります。新しい口を開けるときも同じです。
- 任意であること。値が無ければ、フォークは本家の TurboWarp と同じに動きます。補助関数は空の既定値(
[]、{}、null)を返します。
- GUI 内部の値は引数で受け取ること。ホストはフォークの reducer や UI を import しません。Modal、遅延読み込みの ScratchBlocks、logger、Redux の action creator は、フォークが呼び出し時に渡します(例:
createCancelSecurityLoad(initialState, defaultProjectId, LoadingState, integration))。
- 付けたものは外せること。何かを付ける口は
{dispose()} を返します。フォークは VM が替わるたびに前の戻り値を dispose() してから呼び直し、戻り値の中は見ません。
- VM ごとに同じサービスを返すこと。操作欄のように二か所から読まれるサービスは、VM をキーにした
WeakMap で1つに保ちます。
- CSS の口は作らないこと。本家の CSS モジュールのクラス名はビルド時にハッシュされ、外から狙えません。新しい部品は自分のクラスとスタイルをパッケージ側に持ち、色は GUI の公開テーマ変数を使います。
- 独自のファイルをフォークに足さないこと。フォークに残すのは口を呼ぶ行だけです。テストもフォークには足さず、このリポジトリの
test/ とパッケージ側に置きます。
5. ワークスペースへの取り付け
attachToWorkspace は、Blockly や VM に付く機能をまとめて取り付ける口です。アドオンの userscript に最も近い形をしています。
図10取り付けと取り外しの順序はフォークが持ち、何を付けるかは統合オブジェクトが決めます。
| context の項目 | 内容 |
vm | 今の VM。替わるたびに呼び直される |
locale | 表示言語(ja、en など) |
document | エディターの document。別ウィンドウにも対応するため window.document を直接使わない |
getBlockly | ScratchBlocks を返す関数。読み込まれるまで待つ |
getMessages | 今の言語の翻訳を返す関数 |
ScratchBlocks が使えるようになった時点で一度だけ行う登録(ブロックの形、右クリックメニュー、パレットへの項目の追加)は、registerBlockShapes(ScratchBlocks) に置きます。アドオンの起動を待つ必要があるときは、そのアドオンの id を awaitStartupAddons に書きます(今は editor-searchable-dropdowns)。
6. 実行時の拡張に公開する API
非サンドボックスの拡張は Scratch.gui から次の値を読めます。本家にある getBlockly と getBlocklyEagerly に加え、ホストが値を渡したときだけ次の項目が生えます。値が無いときは undefined のままなので、拡張は「このビルドには無い」と判断できます。
| 項目 | 内容 |
Scratch.gui.stageControls | ステージの操作欄にボタンを足す API。addButton({id, label, text?, icon?, onClick}) が {update, focus, dispose} を返す |
Scratch.gui.blockEditors | フォーム形式のブロック編集サービス |
Scratch.gui.createPreviewVm() | 作品とは独立したプレビュー用の VM を作る |
図11どちらの経路から登録しても同じサービスに入り、Editor のメニューバーに並びます。Editor 自身は個々の拡張を知りません。
addButton で足したボタンは visible: false で始まります。表示するには update({visible: true}) を呼びます。update で変えられるのは label、text、visible、expanded の4つです。展開したときに見せる要素は、サービスの placePanel(element) でホストに渡します。要素の持ち主は登録した側で、ホストは取り付けるだけです。
7. 起動時フラグ
機能の有効・無効は、ページの URL クエリ twPlus… で起動時に一度だけ決まります。config/web-startup-feature-flags.ts が URL を読み、config/feature-flags.ts の readStartupFeatureFlags が凍結したオブジェクトを返します。作品のデータからこの値を受け取ってはいけません。
図12フラグは起動時に URL から一度だけ読み、以後は変わりません。
| 既定 | フラグ(twPlus を省略) |
有効(=0 で無効) | ExtensionLoader ProjectTools PayloadFormats Splitter StandardLayout(Web の既定) PaletteDeletePreview PaletteCategoryReorder ContextMenuIcons LocalGit WorkspaceMinimap StageCodePrewarm |
無効(=1 で有効) | SpriteProperties WindowLayout HorizontalBlockPalette StageDisplayMenu(StandardLayout のときは有効) |
| 他のフラグが前提(既定で有効) | ManualProjectURI(ExtensionLoader が必要)、ExtensionFileMenu(StandardLayout が必要) |
他のフラグが前提(=1 で有効) | GitRestoreVersion(LocalGit が必要)、BackpackPopout InternalWindows(WindowLayout が必要)、StagePanel TargetList SpriteCodeWindows(WindowLayout と InternalWindows が必要) |
8. 同梱アドオンとの関係
- 既定の有効化:
site/addon-manifests.ts が、webpack の alias でフォークの generated/addon-manifests を置き換えます。「おすすめ」タグの付いたアドオンと hide-delete-button、block-palette-icons を既定で有効にし、hide-flyout は無効のままにします。利用者が設定画面で選んだ値が優先されます。
- 起動待ち:
awaitStartupAddons に書いたアドオンは、実行器がその起動を待ってから次へ進みます。
- 右クリックメニュー:
api.js の createBlockContextMenu は、項目を確定する前に finalizeHostTransferMenu を通します。スクリプト転送のメニューはここで足されます。
9. 検証
| コマンド | 確かめること |
npm test | このリポジトリと packages/ の単体テスト。test/plus-integration.test.ts が統合オブジェクトの各項目を固定する |
E2E_GUI_ROOT=… npm run test:gui-integration | 固定したフォークに本家に無いファイルが無いこと、口の意味、移設したテスト |
npm run typecheck | 型検査 |
E2E_GUI_ROOT=… npm run build:pages | 配信ビルド。build-info.json の revision と siteRevision が今の HEAD と site/source.json に一致すること |
npm run test:e2e | Playwright による統合 E2E |
E2E_GUI_ROOT
固定したフォークを checkout したディレクトリの絶対パスを指定します。CI と同じく .host/scratch-gui に置くのが基本です。checkout の HEAD は site/source.json の guiRevision と一致している必要があり、食い違うと test:gui-integration は両方の revision を示して止まります。
1. 置き場所を決める
最初に、機能がどの経路でエディターに届くかを決めます。下の図の上から順に当てはめ、最初に「はい」になったところが置き場所です。
図13A〜C はフォークを変えずに済みます。D だけがフォークへの変更を伴うので、先に A〜C で届かないことを確かめます。
2. 作業の順序
実装はパッケージのリポジトリで先に公開し、Editor はその npm 公開版に上げてからつなぎます。新しい口が要るときは、フォークの変更を並行して進め、Editor で SHA を固定します。
図14上流から下流へ一方向に進みます。Editor の変更は、公開された版を使うところから始まります。
3. 手順
-
置き場所と口を決める
図13で A〜D のどれに当たるかを決め、使う口を仕様書の口の一覧から選びます。実装がどのパッケージに入るかもこの時点で決めます。Editor の packages/ にある既存の実装は移行途中のもので、新しい機能の置き場所にはしません。
-
パッケージに実装する
turbowarp-plus-packages の該当パッケージ(無ければ新しいパッケージ)に実装します。GPL のコードは import せず、エディターから受け取るものはすべて引数でもらいます。付ける機能は {dispose} を返します。
// パッケージ側:document と翻訳を受け取り、外す手段を返す
export const attachSpriteBadges = (hostDocument, {vm, getMessages}) => {
const onTargetsUpdate = () => render(hostDocument, vm, getMessages());
vm.on('targetsUpdate', onTargetsUpdate);
return {
dispose () {
vm.removeListener('targetsUpdate', onTargetsUpdate);
clear(hostDocument);
}
};
};
スタイルはパッケージの中に自分のクラスで持ち、色は GUI の公開テーマ変数を使います。単体テストもパッケージに置きます。
-
版を上げて npm に公開する
turbowarp-plus-packages の npm run bump(上流から)または npm run bump:downstream(下流へ伝える)で版を決め、v<版> のタグで npm に公開します。tgz を Editor に同梱する方法は使いません。
-
(D のときだけ)フォークに口を開ける
本家にもあるファイルの中に、口を呼ぶ行を足します。値が無いときは本家どおりに動くように書きます。
// scratch-gui フォーク:既存ファイルに足すのはこの形の行だけ
const SpriteBadges = getHostIntegration()?.SpriteBadges || null;
// ...
{SpriteBadges ? <SpriteBadges vm={vm} /> : null}
新しいファイルやテストはフォークに足しません。口の意味は Editor 側のテストで押さえます。統合オブジェクトの項目として読ませる場合は、site/gui-integration/lib/tw-plus-integration-points.ts に既定値を返す host… 補助関数を足します。
-
Editor で版と SHA を上げる
package.json と package-lock.json を npm 公開版に上げます。D のときは site/source.json のフォークの SHA を、口を開けたコミットの40桁 SHA に替えます。
-
site/ でつなぐ
C の場合は site/plus-integration.ts の attachToWorkspace に1行足します。
attached.push(attachSpriteBadges(hostDocument, {vm, getMessages}));
B と D で画面の部品を渡す場合は、site/gui-integration/index.ts から export します。起動時フラグで切り替えたい場合は、config/feature-flags.ts と config/web-startup-feature-flags.ts に twPlus… を1つずつ足し、フラグが無効のときは何も渡さないようにします。
// site/gui-integration/index.ts
export const SpriteBadges = startup.spriteBadges ? SpriteBadgesView : undefined;
-
A の場合:拡張から操作欄に登録する
実行時の拡張は、Editor の内部を知らずに操作欄を使えます。Scratch.gui.stageControls が無いビルドでは何もしないようにします。
const controls = Scratch.gui?.stageControls;
if (controls) {
const button = controls.addButton({
id: 'my-camera.calibrate', label: 'カメラを較正する', icon: '◎',
onClick: () => startCalibration()
});
button.update({visible: true}); // 追加直後は非表示
}
パッケージとして配る拡張は、@kubohiroya/turbowarp-app-shell の操作欄を使うと、ツールバーの無い単一 HTML のプレイヤーでも同じ操作が出ます。
-
検証する
npm test、npm run typecheck、npm run test:gui-integration を通し、E2E_GUI_ROOT=… npm run build:pages でビルドし直してから画面を確認します(E2E_GUI_ROOT は仕様書の検証を参照)。scratch-gui/build に残っている古い成果物は使いません。build-info.json の revision と siteRevision が今の状態に一致しているか確かめます。
-
PR を出す
変更の内容と確かめたことを書いて PR を出し、CI の結果を見てからマージします。
4. 画面部品の約束
追加機能が出すダイアログやパネルは、次の形に揃えます。
- 下部の操作ボタン:右端にまとめ、キャンセルを左、保存・適用などの主操作を右に置きます。共通の
tw-plus-dialog-actions を使い、ボタンの data-dialog-action に cancel、close、confirm、delete のいずれかを指定します。文字のラベルを残し、キャンセルと閉じるには×、保存・確定にはチェック、削除にはごみ箱のアイコンを添えます。
- 見出しの横の閉じるボタン:パネルやダイアログの右上に置く閉じるボタンには「閉じる」「Close」の文字を出さず、×アイコンだけにします。名前は翻訳した
title と aria-label で示します。
- 文字列:表示する文字列は
ja と en の翻訳を持ちます(ひらがな表示が要るものは ja-hira も)。
5. 出す前の確認
- 実装の本体はパッケージにあり、Editor の変更は版の更新と数行の接続に収まっている
- 付けたものはすべて
dispose() で外れる。VM が替わっても二重に付かない
- 値を渡さないとき、フォークは本家の TurboWarp と同じに動く
- GUI 内部の値は引数で受け取り、フォークのモジュールを import していない
- 表示する文字列は
ja と en の翻訳を持つ(ひらがな表示が要るものは ja-hira も)
- 起動時フラグは URL からだけ読み、作品のデータから読んでいない
- 単体テストはパッケージに、口のテストは Editor の
test/ にある
- ビルドし直した成果物で画面を確かめた
してはいけないこと
- フォークに新しいファイル、独自のテスト、本家のクラス名を狙う CSS を足す
- 本家の DOM をクラス名で探して書き換える(それはアドオンの方式で、口があるなら口を使う)
- パッケージから GPL のコード(フォークや Editor の
site/)を import する
- パッケージを tgz で Editor に同梱する
6. このリポジトリの保守者向けの注意
保守者・AI エージェント向け
ここからは、turbowarp-plus-editor に直接変更を入れる人(AI エージェントを含む)向けの運用規則です。正本は AGENTS.md です。
- PR に
--auto を付けない。このリポジトリの PR には必須のチェックが無く、--auto を付けると CI を待たずにすぐマージされる
- OS のマウスカーソルを動かす E2E(
TW_PLUS_NATIVE_INPUT=1、test:e2e:native)を、明示的に依頼されていないのに走らせない。一般的な E2E の実行や PR の検証の依頼には含まれない
関連文書:docs/plus-integration-sockets.md(口の設計の経緯)、docs/gui-integration/README.md(移設とビルド時の接続)、docs/architecture.md(責務と配信)。