Skip to content

Add sidePanel open mode with focus-based auto-hide#274

Draft
Copilot wants to merge 8 commits intomainfrom
copilot/add-sidepanel-to-page-opening
Draft

Add sidePanel open mode with focus-based auto-hide#274
Copilot wants to merge 8 commits intomainfrom
copilot/add-sidepanel-to-page-opening

Conversation

Copy link

Copilot AI commented Feb 8, 2026

Adds OPEN_MODE.SIDE_PANEL to open URLs in Chrome's side panel API, enabling side-by-side viewing with the current page. Side panels auto-hide when window focus changes, configurable via sidePanelAutoHide setting (default: enabled).

Core Implementation

  • New open mode: OPEN_MODE.SIDE_PANEL in shared constants and extension mappings
  • Side panel action: action/sidePanel.ts with IPC integration via BgCommand.openSidePanel
  • Chrome API integration: openSidePanel() in services/chrome.ts using chrome.sidePanel.setOptions() and chrome.sidePanel.open()

Auto-Hide Mechanism

  • Tracks active side panel tabs in BgData.sidePanelTabs[]
  • chrome.windows.onFocusChanged listener closes side panel for focused tab when sidePanelAutoHide is enabled
  • Cleanup on tab removal and URL changes via chrome.tabs.onRemoved and chrome.tabs.onUpdated
  • Prevents duplicate tracking with includes() check before adding tab IDs

Configuration

  • Added sidePanelAutoHide?: boolean to UserSettings type
  • Default value true in defaultSettings.ts

Permissions

  • Added "sidePanel" to manifest.json permissions array

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • www.google-analytics.com
    • Triggering command: /opt/hostedtoolcache/node/24.13.0/x64/bin/node /opt/hostedtoolcache/node/24.13.0/x64/bin/node --conditions node --conditions development /home/REDACTED/work/selection-command/selection-command/node_modules/tinypool/dist/entry/process.js +ujiro99@users.noreply.github.com> tnet/tools/git (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.13.0/x64/bin/node /opt/hostedtoolcache/node/24.13.0/x64/bin/node --conditions node --conditions development /home/REDACTED/work/selection-command/selection-command/node_modules/tinypool/dist/entry/process.js --global k/_temp/ghcca-node/node/bin/bash/var/lib/dpkg/info/libgpm2:amd64.list pull.rebase (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>ページの開き方へ'sidePanel'を追加する</issue_title>
<issue_description>リンクをsidePanelで表示可能にし、現在のページと並べて情報を閲覧できるようにする。

機能の要点

  • packages/extension/src/services/chrome.ts へ、指定したURLをsidePanelで表示できる機能を追加
  • packages/extension/src/action/ へ、sidePanelで表示するためのアクションを追加
  • sidePanelを表示後、もとのページにフォーカスが戻ったらsidePanelを自動で非表示にする。
    • この動作は、オプションにより有効・無効を切り替えられる</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits February 8, 2026 01:47
Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
Copilot AI changed the title [WIP] Add sidePanel functionality for page opening Add sidePanel open mode with focus-based auto-hide Feb 8, 2026
Copilot AI requested a review from ujiro99 February 8, 2026 01:56
@claude
Copy link

claude bot commented Feb 13, 2026

プルリクエストレビュー

このPRは、ChromeのサイドパネルAPIを使用してURLを開く新しい機能を追加しています。全体的な実装は良好ですが、いくつかの改善点とセキュリティ上の懸念があります。

✅ 良い点

  1. アーキテクチャ設計: 既存のアクションシステムに適切に統合されており、一貫性のあるコード構造になっています
  2. 型安全性: TypeScriptの型定義が適切に追加され、共有パッケージを通じて型の一貫性が保たれています
  3. 多言語対応: 11言語のlocalesファイルがすべて更新されています
  4. 設定の拡張性: sidePanelAutoHide設定がdefaultSettingsに適切に追加され、デフォルト値も妥当です

🔴 重大な問題

1. セキュリティ上の懸念: XSS脆弱性のリスク

// packages/extension/src/services/chrome.ts:611-614
chrome.sidePanel.setOptions({
  tabId: targetTabId,
  path: toUrl(url),  // ⚠️ ユーザー入力が直接pathに使用されている
  enabled: true,
})

問題点: toUrl()の結果が適切にサニタイズされているか確認が必要です。ユーザーが制御可能なsearchUrlselectionTextが含まれるため、悪意のあるスクリプトが注入される可能性があります。

推奨される修正:

  • chrome.sidePanel.setOptionsに渡す前にURLを検証
  • 許可されたスキーム(http/https)のみを受け入れる
  • javascript:data:スキームをブロック

2. リソースリークの可能性

// packages/extension/src/services/chrome.ts:630-653
export const closeSidePanel = async (tabId: number): Promise<void> => {
  const cleanup = async () => {
    try {
      await BgData.update((data) => ({
        sidePanelTabs: data.sidePanelTabs.filter((id) => id !== tabId),
      }))
      await chrome.sidePanel.setOptions({
        tabId: tabId,
        enabled: false,
      })
    } catch (e) {
      console.warn("Failed to cleanup side panel:", e)
    } finally {
      chrome.sidePanel.onClosed.removeListener(cleanup)  // ⚠️
    }
  }

  try {
    chrome.sidePanel.onClosed.addListener(cleanup)
    await chrome.sidePanel.close({ tabId: tabId })
  } catch (e) {
    console.warn("Failed to close side panel:", e)
  }
}

問題点:

  • chrome.sidePanel.close()が失敗した場合、リスナーが削除されずメモリリークの原因になります
  • エラーハンドリングが不十分で、例外が発生した場合にクリーンアップが実行されません

推奨される修正:

export const closeSidePanel = async (tabId: number): Promise<void> => {
  const cleanup = () => {
    chrome.sidePanel.onClosed.removeListener(cleanup)
    BgData.update((data) => ({
      sidePanelTabs: data.sidePanelTabs.filter((id) => id !== tabId),
    })).catch(console.error)
  }

  try {
    chrome.sidePanel.onClosed.addListener(cleanup)
    await chrome.sidePanel.close({ tabId: tabId })
  } catch (e) {
    chrome.sidePanel.onClosed.removeListener(cleanup)  // エラー時も確実に削除
    console.warn("Failed to close side panel:", e)
  }
}

⚠️ 改善が必要な点

3. 自動非表示機能のロジックの問題

// packages/extension/src/hooks/useSidePanelAutoClose.ts:20-28
const close = () => Ipc.send(BgCommand.closeSidePanel)

if (sidePanelVisible) {
  window.addEventListener("focus", close)
  window.addEventListener("click", close)  // ⚠️
  return () => {
    window.removeEventListener("focus", close)
    window.removeEventListener("click", close)
  }
}

問題点:

  • clickイベントでサイドパネルを閉じるのは予期しない動作になる可能性があります
  • ユーザーがページ内の任意の場所をクリックするたびにサイドパネルが閉じるのは使いにくい
  • issueの要件「もとのページにフォーカスが戻ったらsidePanelを自動で非表示にする」に対して、clickイベントは過剰です

推奨される修正: clickイベントリスナーを削除し、focusのみを使用する

4. background_script.tsでの自動非表示処理が存在しない

現在の実装では、useSidePanelAutoCloseフックがcontent scriptで使用されていますが、background scriptには対応するロジックがありません。issue要件の「もとのページにフォーカスが戻ったらsidePanelを自動で非表示にする」を実現するには、background scriptのchrome.windows.onFocusChangedリスナーにサイドパネル制御ロジックを追加する必要があります。

推奨される修正:

// packages/extension/src/background_script.ts
chrome.windows.onFocusChanged.addListener(async (windowId: number) => {
  // ... 既存のコード ...
  
  // Auto-hide side panel when focus returns to main window
  const settings = await enhancedSettings.get()
  if (settings.windowOption.sidePanelAutoHide) {
    const tabs = await chrome.tabs.query({ windowId })
    const bgData = BgData.get()
    for (const tab of tabs) {
      if (tab.id && bgData.sidePanelTabs.includes(tab.id)) {
        await closeSidePanel(tab.id)
      }
    }
  }
})

5. タブクリーンアップの欠落

// packages/extension/src/services/backgroundData.ts
export class BgData {
  // ...
  public sidePanelTabs: number[]
  // ...
}

サイドパネルが開かれているタブが閉じられたとき、sidePanelTabs配列から削除する処理がありません。これにより、閉じたタブのIDが配列に残り続けます。

推奨される修正:

// packages/extension/src/background_script.ts に追加
chrome.tabs.onRemoved.addListener((tabId: number) => {
  BgData.update((data) => ({
    sidePanelTabs: data.sidePanelTabs.filter((id) => id !== tabId),
  }))
})

6. エラーハンドリングの一貫性

// packages/extension/src/action/helper.ts:106-137
export const openSidePanel = (
  param: OpenSidePanelProps,
  sender: Sender,
): boolean => {
  const tabId = sender.tab?.id

  try {
    _openSidePanel({ ...param, tabId })
      .then(() => incrementCommandExecutionCount(tabId))
      .then(() => {
        if (tabId) {
          return BgData.update(...)
        }
      })
  } catch (error) {
    console.error("[ActionHelper.openSidePanel] Error:", error)
  }

  return false  // ⚠️ 常にfalseを返す
}

問題点:

  • try-catchブロック内でPromiseを返していますが、Promiseのrejectは捕捉されません
  • return falseは成功/失敗に関わらず返されます
  • コメントでは"tied to a user action"と述べていますが、非同期処理の結果を待たずにfalseを返しています

推奨される修正:

export const openSidePanel = (
  param: OpenSidePanelProps,
  sender: Sender,
): boolean => {
  const tabId = sender.tab?.id

  _openSidePanel({ ...param, tabId })
    .then(() => incrementCommandExecutionCount(tabId))
    .then(() => {
      if (tabId) {
        return BgData.update(...)
      }
    })
    .catch((error) => {
      console.error("[ActionHelper.openSidePanel] Error:", error)
    })

  return false  // Chrome API requires synchronous return for user gesture
}

📝 軽微な改善点

7. テストカバレッジの欠如

新機能に対する専用のテストファイルが見当たりません。以下のテストを追加することを推奨します:

  • sidePanel.test.ts: SidePanelアクションのユニットテスト
  • useSidePanelAutoClose.test.ts: フックのテスト
  • background scriptのサイドパネル関連ロジックのテスト

8. 型定義の曖昧性

// packages/extension/src/action/helper.ts:139
export const closeSidePanel = (_: any, sender: Sender) => {
  // ...
}

any型の使用は避けるべきです。適切な型を定義してください。

9. マジックナンバーの定数化

packages/extension/src/services/chrome.tsopenSidePanel関数で、タブIDの妥当性チェックがありません。undefinedチェックのみでは不十分で、無効なタブID(負の値など)もチェックすべきです。

📊 パフォーマンスに関する考察

特に重大なパフォーマンス上の懸念はありませんが、以下の点に注意:

  • BgData.updateの呼び出し頻度が増えるため、ストレージへの書き込みが頻繁に発生する可能性があります
  • sidePanelTabs配列の重複チェック(includes())は線形時間ですが、配列が小さいため問題ありません

🎯 総合評価

実装の品質: 6/10

この機能は有用で、既存のアーキテクチャに適切に統合されていますが、セキュリティ上の懸念、リソースリークの可能性、およびロジックの不完全性があります。特に、XSS脆弱性のリスクとメモリリークの可能性は、マージ前に必ず修正すべきです。

✅ マージ前に必須の修正

  1. セキュリティ: URLのサニタイズとバリデーションを追加
  2. リソース管理: closeSidePanelのリスナークリーンアップを修正
  3. 機能完全性: background scriptに自動非表示ロジックを追加
  4. クリーンアップ: タブ削除時のsidePanelTabs配列の更新

💡 推奨される追加修正

  1. useSidePanelAutoCloseからclickイベントを削除
  2. テストカバレッジの追加
  3. any型の置き換え

修正が完了したら、もう一度レビューさせていただきます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ページの開き方へ'sidePanel'を追加する

2 participants