📸

ブラウザ操作を自動キャプチャして手順書を作るChrome拡張の作り方:Procshot開発記録

に公開

はじめに

「この操作、どうやるんでしたっけ?」

社内システムの使い方を聞かれるたびに、スクショを撮って、赤枠つけて、「ここをクリックします」と書いて...。気づけば30分経っている。

この無駄な時間をなくしたくて、ブラウザ操作を自動でキャプチャして手順書を作るChrome拡張機能を作りました。

https://chromewebstore.google.com/detail/手順キャプチャ-手順書・操作マニュアル自動作成/ieblehdloggcpmkncplccjofeoakhkll

作ったもの

手順キャプチャ(英語名: Procshot)

「撮影開始」ボタンを押して普通にブラウザを操作するだけで、スクショ付きの手順書が自動で完成します。

主な機能

  • 📸 クリック・入力・ページ遷移を自動検出してスクショ撮影

  • ✏️ 「検索ボタンをクリックします」など説明文を自動生成

  • 🎯 クリック位置を枠や矢印で強調

  • 📤 HTML / Markdown / PDF でエクスポート

特徴

完全ローカル動作: スクショや操作データは外部に送信されません

日本語ネイティブ対応: 説明文が自然な日本語で生成されます

$9/月: 競合(Scribe $23、Tango $20)より安い

技術スタック

Chrome Extension (Manifest V3)
├── React 18
├── TypeScript
├── Vite + CRXJS
├── Tailwind CSS
└── IndexedDB (idb-keyval)

サーバーレスで、すべての処理がブラウザ内で完結します。

技術的に面白かったところ

1. スクショのタイミング問題

最初は「クリック後にスクショを撮る」実装にしていましたが、これだと問題が発生しました。

ユーザーがボタンをクリック

ページが遷移

スクショ撮影 ← この時点でクリックしたボタンはもう画面にない!

解決策として、クリック前にスクショを撮るように変更しました。

document.addEventListener('click', async (event) => {
  // 1. クリック前に即座にスクショ
  const screenshot = await captureVisibleTab();
  
  // 2. 要素情報を取得(まだDOMにある状態で)
  const element = event.target;
  const rect = element.getBoundingClientRect();
  
  // 3. ステップとして記録
  saveStep({ screenshot, rect, ... });
  
  // 4. クリックは通常通り実行される
}, true);

2. クリック対象の特定

ユーザーが の中の の中の `` をクリックした時、イベントターゲットはどれになるか?

答えは「場合による」です。イベントバブリングにより、実際にクリックした要素より親要素がターゲットになることがあります。

そこで、意味のある要素を探索するロジックを追加しました。

function findMeaningfulElement(target: Element): Element {
  const genericTags = ['DIV', 'SPAN', 'BODY', 'SECTION'];
  
  // ターゲットが汎用要素なら、子要素から意味のある要素を探す
  if (genericTags.includes(target.tagName)) {
    const button = target.querySelector('button, a, input, [role="button"]');
    if (button) return button;
  }
  
  return target;
}

3. 説明文の自動生成

クリックした要素から「何をクリックしたか」を説明するテキストを生成します。

優先順位:

aria-label 属性

placeholder 属性

innerText(アイコン要素を除外)

  • タグ名からの推測(「ボタンをクリックします」)
function getElementText(element: Element): string {
  // aria-labelを優先
  const ariaLabel = element.getAttribute('aria-label');
  if (ariaLabel) return ariaLabel;
  
  // placeholderをチェック
  if (element instanceof HTMLInputElement && element.placeholder) {
    return element.placeholder;
  }
  
  // innerTextからアイコンを除外して取得
  const clone = element.cloneNode(true) as Element;
  clone.querySelectorAll('svg, [class*="icon"]').forEach(el => el.remove());
  return clone.textContent?.trim() || '';
}

ただし、これだと「open_in_new」(Material Iconのaria-label)などが混入することがあったので、除外パターンも追加しています。

4. SPAのページ遷移検出

ReactやVueなどのSPAでは、従来の beforeunload イベントが発火しません。代わりに History API を監視します。

// pushState/replaceState をラップして検出
const originalPushState = history.pushState;
history.pushState = function(...args) {
  originalPushState.apply(this, args);
  handleNavigation();
};

// popstate(ブラウザの戻る/進む)も検出
window.addEventListener('popstate', handleNavigation);

5. 撮影中のオーバーレイUI

撮影中であることをユーザーに伝えつつ、オーバーレイ自体がスクショに映り込まないようにする必要がありました。

const overlay = document.createElement('div');
overlay.setAttribute('data-procshot-ignore', 'true');
document.body.appendChild(overlay);

// スクショ撮影時にオーバーレイを一時的に非表示
async function captureScreenshot() {
  overlay.style.display = 'none';
  const screenshot = await chrome.tabs.captureVisibleTab();
  overlay.style.display = 'block';
  return screenshot;
}

苦労したところ

Manifest V3 の制約

Chrome拡張のManifest V3では、Service Workerが非アクティブ時に停止します。これにより、録画状態の管理が複雑になりました。

// Service Workerが再起動するたびに状態を復元
chrome.storage.local.get('recordingState', (result) => {
  if (result.recordingState?.isRecording) {
    // 録画を再開
    resumeRecording(result.recordingState);
  }
});

権限の最小化

Chrome Web Storeの審査では、権限の正当性が厳しくチェックされます。`` 権限を使う理由を明確に説明する必要がありました。

この拡張機能は、ユーザーが閲覧しているWebページでスクリーンショットを撮影し、クリック位置を検出するために、すべてのURLへのアクセス権限が必要です。

今後の予定

AI による説明文の改善(Gemini API 連携)

インタラクティブ再生(実際のページ上でガイド表示)

チーム共有機能

おわりに

個人開発で14個目のChrome拡張機能になりました。

「こういう機能がほしい」「ここがおかしい」などフィードバックいただけると嬉しいです!

https://chromewebstore.google.com/detail/手順キャプチャ-手順書・操作マニュアル自動作成/ieblehdloggcpmkncplccjofeoakhkll

S-Hub では、生産性向上のためのツールを開発しています。

他の拡張機能もぜひチェックしてみてください。

https://s-hub.pages.dev

作った拡張機能(2025年2月現在:15個)

🎯 生産性ツール

PromptStash - AIプロンプトを保存・管理(19サービス対応)

DataPick - Webページからデータを抽出

DataBridge - ページ間データ転送

SnippetVault - コードスニペット管理

ReadMark - 読書位置を記憶

🧘 デジタルウェルビーイング

YouTube Shorts Killer - YouTubeショートを非表示

X Detox - X(Twitter)のノイズを消す

ZenRead - 集中読書モード

🛒 EC・せどり

楽天セラーズ・アナリティクス - 楽天市場の出品者分析

Arbitra - 価格比較ツール

🔧 Web便利ツール

Japanese Font Finder - Webサイトの日本語フォントを特定

TVerPlus - TVerの視聴体験を改善

Yahoo快適 - Yahoo!の閲覧体験改善

🏠 不動産

物件賃貸分析 - 賃貸物件の分析

物件購入分析 - 購入物件の分析

すべての拡張機能の詳細 → S-Hub

GitHubで編集を提案

Discussion