出前館で店を選んでいると、店名からは実店舗なのかデリバリー専用ブランドなのか分からないことがよくある。いわゆる「ゴーストレストラン」——実店舗を持たず、間借りキッチンや既存店の厨房から複数のブランド名で出前だけを展開している業態——が一覧に紛れ込んでいて、選ぶたびに個別に住所を調べる羽目になっていた。この手間を減らしたくて、demaecan-no-ghostsというTampermonkeyユーザースクリプトを作った。

できること

  • 店舗カードに情報アイコン — 一覧ページ(「過去に注文したお店」カルーセルも含む)の各店舗カード右上にアイコンが付く。クリックまたはホバーすると、店名・住所(初回のみ取得)・Googleマップへのリンク・Google検索へのリンクが載ったポップオーバーが開く。
  • ゴースト / 実店舗の判定を保存 — ポップオーバーや店舗ページから、その店を👻(ゴースト)か🏠(実店舗)として判定できる。判定は保存され、その店が出てくるすべての箇所でアイコンの絵柄に反映される。
  • 判定済みの店を一覧からフィルタ — 画面右下のトグルで、ゴースト判定済みの店を一覧から非表示にできる。
  • 店舗ページの判定パネル — 店舗自身のページを開くと画面左下に判定パネルが出る。出前館はSPAなので、画面遷移してもパネルがちゃんと追従するようにしてある。

インストールはTampermonkeyを入れた状態でこのURLを開くだけ。あとは自動更新される。

なぜ作ったか

出前館の店舗一覧は、実店舗の写真や住所が載っているように"見える"カードと、ゴーストレストランのカードが同じ見た目で並ぶ。判別する手がかりはほぼ店名だけで、結局は店ごとに検索して確認する作業が発生する。この手作業を「一度判定したら覚えておいてくれる」仕組みに置き換えたかったのが動機だ。

判定自体は主観に委ねている。スクリプトが自動でゴーストかどうかを推定するのではなく、住所を見やすく出す・調べやすくする・一度判定したら記憶する、という「意思決定を助けるためのUI」に徹している。

技術的なところ

TypeScript + Vite + Vitest(カバレッジ計測込み)+ ESLintという、割とふつうの構成でビルドしてユーザースクリプト1ファイルに固める形にしている。出前館側はSPAなので、店舗ページへの遷移やカードの動的な差し替えにDOM監視で追従させる必要があり、このあたりが一番手を焼いたところだった。

開発フローはOpenSpecで変更を提案 → 実装 → アーカイブ、というサイクルを回している。openspec/changes/にproposalとdesignとtasksを書いてから実装に入るので、あとから「なぜこの挙動にしたか」を追いやすい。実際、右上アイコンの絵柄出し分けやホバー範囲の調整など、細かい挙動修正もすべてこのサイクルの中でproposalとして残っている。

今後

店名だけからゴーストらしさを判定するヒューリスティック(候補が1件なら実店舗寄り、2件以上絡むなら除外、など)をどこまでスクリプト側に持たせるかは検討中。ユーザーの判定を尊重しつつ、初見の店でも多少のヒントが出せるとさらに楽になりそうだ。

リポジトリはkuchida1981/demaecan-no-ghosts。ISCライセンスなので、気になった人は覗いてみてほしい。