Skip to content

Fix/clipboard at sidepanel#339

Merged
ujiro99 merged 4 commits intomainfrom
fix/clipboard-at-sidepanel
Mar 21, 2026
Merged

Fix/clipboard at sidepanel#339
ujiro99 merged 4 commits intomainfrom
fix/clipboard-at-sidepanel

Conversation

@ujiro99
Copy link
Owner

@ujiro99 ujiro99 commented Mar 21, 2026

ショートカットキーからの起動時でかつSidePanelモードの場合に、クリップボードの内容が取得されていない不具合を修正する

@ujiro99 ujiro99 requested a review from Copilot March 21, 2026 04:41
@ujiro99 ujiro99 changed the base branch from main to copilot/add-instant-command-feature March 21, 2026 04:42
@ujiro99 ujiro99 changed the base branch from copilot/add-instant-command-feature to main March 21, 2026 04:42
@claude
Copy link

claude bot commented Mar 21, 2026

PR レビュー: Fix/clipboard at sidepanel

概要

バグの根本原因と修正方針は正確です。SidePanel.execute においてクリップボード取得処理が欠けており、toUrlclipboardText が渡されていなかったことが原因でした。


✅ 良い点

  • packages/extension/src/action/sidePanel.ts:18-33: クリップボード取得を try-catch で囲み、失敗してもサイドパネルを開く処理を継続させるアプローチは適切です。
  • packages/extension/src/background_script.ts:609-614: chrome.tabs.query の位置をコマンド取得・セレクションテキスト取得の後に移動したことで、早期 return 後に不要なタブ取得が行われなくなりました。
  • packages/extension/src/services/chrome.ts:700-704: chrome.sidePanel.open の try-catch 追加は適切です。

⚠️ 問題点・改善提案

1. トースト通知送信時のエラーハンドリング漏れ(潜在的バグ)

packages/extension/src/action/sidePanel.ts:27-32

} catch (e) {
  console.warn("Failed to read clipboard text:", e)

  const tabId = await Ipc.getTabId()          // ← エラーハンドリングなし
  await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, { ... })  // ← エラーハンドリングなし
}

SidePanel.execute はバックグラウンドスクリプトから直接呼ばれることがあります(background.ts:actionsForBackground 経由)。この経路は enableSendTabfalse の場合(タブが chrome:// ページなど)に実行されます。このとき、Ipc.getTabId()Ipc.sendTab() も失敗する可能性があります。

また、Ipc.getTabId()undefined を返す可能性があり、その場合 sendTab に無効な tabId が渡されます。

提案: トースト送信処理をさらに try-catch で囲む。

} catch (e) {
  console.warn("Failed to read clipboard text:", e)
  try {
    const tabId = await Ipc.getTabId()
    if (tabId) {
      await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, { ... })
    }
  } catch (toastError) {
    console.warn("Failed to show clipboard error toast:", toastError)
  }
}

2. chrome.sidePanel.open のエラーがサイレントになった点

packages/extension/src/services/chrome.ts:700-704

try {
  await chrome.sidePanel.open({ tabId })
} catch (e) {
  console.error("Failed to open side panel:", e)
}

return { tabId }  // ← 失敗しても tabId を返す

以前は例外が伝播していましたが、現在はエラーをキャッチして { tabId } を返します。呼び出し元がサイドパネルの開封失敗を検知できなくなりました。意図的であれば問題ありませんが、将来的な混乱を避けるためにコメントを追加することを推奨します。


3. 条件の重複(軽微)

packages/extension/src/action/sidePanel.ts:21packages/extension/src/lib/utils.ts:70

// sidePanel.ts
if (useClipboard && isEmpty(selectionText)) {
  clipboardText = await navigator.clipboard.readText()
}

// utils.ts (toUrl 内)
if (useClipboard && isEmpty(text)) {
  text = clipboardText ?? ""
}

sidePanel.ts の条件と toUrl 内部の条件が同一ロジックです。コードの重複自体はバグではありませんが、将来どちらかが変更されたときに不整合が生じるリスクがあります。現状では許容範囲内です。


🔍 テストカバレッジ

この PR にテスト変更が含まれていません。以下のケースのユニットテスト追加を検討してください:

  • navigator.clipboard.readText() が成功した場合にクリップボードテキストが toUrl に渡されること
  • navigator.clipboard.readText() が失敗した場合にトースト通知が送られ、サイドパネルが clipboardText = "" で開かれること
  • useClipboardfalse の場合にクリップボード読み取りが実行されないこと

まとめ

バグ修正の本質(clipboardTexttoUrl に渡す)は正しく実装されています。主な懸念点はクリップボードエラー時のトースト送信処理でのエラーハンドリング漏れです(バックグラウンドコンテキストでのエラーが unhandled rejection になる可能性)。テストカバレッジの追加も検討いただけると、リグレッションリスクが下がります。

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

ショートカットキー起動かつ SidePanel モード時に、選択テキストが無いケースでクリップボード内容がURL補間に使われず取得できていなかった問題の修正を目的としたPRです。

Changes:

  • SidePanel 実行時に、必要に応じて navigator.clipboard.readText() でクリップボードを直接取得して toUrl(..., clipboardText) に渡すよう変更
  • ショートカット処理で active tab 取得タイミングを後ろに移動
  • 拡張のバージョンを 0.17.0 に更新

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/extension/src/services/chrome.ts sidePanel の open 処理に try/catch を追加し、tabId 参照を簡略化
packages/extension/src/background_script.ts ショートカット処理で active tab 取得を後段へ移動
packages/extension/src/action/sidePanel.ts SidePanel 実行時にクリップボード取得→URL生成へ反映、失敗時トースト通知を追加
packages/extension/package.json extension パッケージのバージョン更新
packages/extension/manifest.json manifest のバージョン更新
package.json monorepo ルートのバージョン更新

Comment on lines 27 to 32
const tabId = await Ipc.getTabId()
await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, {
title: t("clipboard_error_title"),
description: t("clipboard_error_description"),
action: t("clipboard_error_action"),
})
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • SidePanel コンテキストでは BgCommand.getTabIdsender.tab?.id を返す実装のため、Ipc.getTabId()undefined になり得ます(実際、background_script 側にも「Side panel pages have no tab.id」とコメントがあります)。
  • その状態で Ipc.sendTab(tabId, TabCommand.showToast, ...) を呼ぶと chrome.tabs.sendMessageundefined が渡り、トースト表示に失敗します。
  • ここは tabId を前提にしない手段(例: Ipc.callListener(TabCommand.showToast, ...) でローカルに発火、または Ipc.getActiveTabId() で取得した tabId に送る等)に切り替えてください。
Suggested change
const tabId = await Ipc.getTabId()
await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, {
title: t("clipboard_error_title"),
description: t("clipboard_error_description"),
action: t("clipboard_error_action"),
})
// Side panel pages may not have an associated tab.id, so fall back to the active tab.
const currentTabId = (await Ipc.getTabId()) ?? (await Ipc.getActiveTabId())
if (currentTabId != null) {
await Ipc.sendTab<ShowToastParam>(currentTabId, TabCommand.showToast, {
title: t("clipboard_error_title"),
description: t("clipboard_error_description"),
action: t("clipboard_error_action"),
})
} else {
console.warn("Unable to determine tabId for showing clipboard error toast.")
}

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@codecov
Copy link

codecov bot commented Mar 21, 2026

Codecov Report

❌ Patch coverage is 21.05263% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 25.21%. Comparing base (2cc7e2f) to head (adaa48f).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
packages/extension/src/action/sidePanel.ts 12.00% 22 Missing ⚠️
packages/extension/src/services/chrome.ts 0.00% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #339      +/-   ##
==========================================
- Coverage   25.22%   25.21%   -0.02%     
==========================================
  Files         324      324              
  Lines       31816    31836      +20     
  Branches     1557     1557              
==========================================
+ Hits         8027     8028       +1     
- Misses      23789    23808      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ujiro99 ujiro99 merged commit 45cae14 into main Mar 21, 2026
4 checks passed
@ujiro99 ujiro99 deleted the fix/clipboard-at-sidepanel branch March 21, 2026 04:49
@claude
Copy link

claude bot commented Mar 21, 2026

コードレビュー

PR の目的(ショートカットキー + SidePanel モードでクリップボード内容が取得されない不具合の修正)は明確で、アプローチの方向性は正しいと思います。ただし、いくつか懸念点があります。


🔴 重大な問題

1. クリップボードエラー時のトースト送信に対するエラーハンドリング不足

ファイル: packages/extension/src/action/sidePanel.ts (行 27–33)

} catch (e) {
  console.warn("Failed to read clipboard text:", e)

  const tabId = await Ipc.getTabId()          // undefined を返す可能性がある
  await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, { ... })
}

SidePanel コンテキストでは Ipc.getTabId()undefined を返す可能性があります(SidePanel ページはタブに紐づかないため)。tabIdundefined のまま Ipc.sendTab() を呼び出すと、chrome.tabs.sendMessage(undefined, ...) が実行され、エラーが握りつぶされるかエラーが伝播してクラッシュする恐れがあります。

さらに、このキャッチブロック内の getTabId()sendTab() 自体は try-catch で囲まれていないため、失敗した場合に Unhandled Promise Rejection になります。

修正案:

} catch (e) {
  console.warn("Failed to read clipboard text:", e)
  try {
    const tabId = await Ipc.getTabId() ?? await Ipc.getActiveTabId()
    if (tabId) {
      await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, {
        title: t("clipboard_error_title"),
        description: t("clipboard_error_description"),
        action: t("clipboard_error_action"),
      })
    }
  } catch (toastError) {
    console.warn("Failed to send toast notification:", toastError)
  }
}

🟡 中程度の問題

2. openSidePanel の呼び出し元が throw に対応しているか要確認

ファイル: packages/extension/src/services/chrome.ts (行 694–699)

try {
  await chrome.sidePanel.open({ tabId })
} catch (e) {
  console.error("Failed to open side panel:", e)
  throw e  // ← 今回の変更で追加
}

throw e の追加自体は正しい変更です。しかし、呼び出し元(おそらく ActionHelper.openSidePanel または background_script.ts 内の該当箇所)がこの例外を適切にキャッチしているか確認が必要です。例外が伝播した場合、後続の BgData 更新や URL 登録処理がスキップされる可能性があり、状態の不整合につながることがあります。

3. クリップボードエラー後も実行が継続する

ファイル: packages/extension/src/action/sidePanel.ts (行 35–50)

クリップボード読み取りに失敗した場合、clipboardText"" のままで toUrl() が呼び出され、クリップボードの内容なしで URL が構築されます。ユーザーがクリップボード使用を意図していた場合、空の状態でコマンドが実行されてしまう可能性があります。

トースト通知を出すだけでなく、return で処理を中断するオプションも検討してください。


🟢 改善点(軽微)

4. background_script.ts のリファクタリングは適切

ファイル: packages/extension/src/background_script.ts (行 580–618)

chrome.tabs.queryenableSendTab の計算を早期リターンのに移動した変更は正しいです。早期リターンが発生するケースで不要なタブ取得を避けられるようになり、効率的です。

5. targetTabId の削除はクリーン

ファイル: packages/extension/src/services/chrome.ts (行 684–690)

冗長な targetTabId エイリアスの削除は読みやすさの向上です。問題ありません。


🔵 テストカバレッジ

クリップボード読み取りの成功・失敗パス、および toUrl()clipboardText を渡すパスについて、テストが追加されていません。特に以下のケースのテストが望ましいです:

  • useClipboard=true かつ selectionText="" の場合にクリップボードが読まれる
  • クリップボード読み取りが失敗した場合のフォールバック動作
  • tabIdundefined の場合のトースト送信の安全性

まとめ

区分 内容
🔴 重大 Ipc.getTabId()undefined を返した場合の未処理エラー(sidePanel.ts 27–33行)
🟡 中程度 throw e 追加後の呼び出し元エラーハンドリングの確認(chrome.ts 694–699行)
🟡 中程度 クリップボードエラー時の処理継続の是非(sidePanel.ts 35行以降)
🟢 良好 background_script.ts のリファクタリング、targetTabId 削除
🔵 要追加 クリップボード関連のユニットテスト

特に Ipc.getTabId()undefined ケースは、修正しようとしている問題(SidePanel 環境での動作)と直接関係するため、早急な対応をお勧めします。

@claude
Copy link

claude bot commented Mar 21, 2026

PR #339 コードレビュー

PRの概要

ショートカットキーからコマンドを起動し、openModeSidePanel の場合にクリップボードの内容がURLに反映されない不具合の修正。sidePanel.ts にクリップボード取得処理が欠けていたことが根本原因で、修正方針は正確です。


潜在的なバグ・問題点

🔴 [重要] catch ブロック内のエラーが未処理

packages/extension/src/action/sidePanel.ts:27-32

} catch (e) {
  console.warn("Failed to read clipboard text:", e)

  const tabId = await Ipc.getActiveTabId()  // エラーハンドリングなし
  await Ipc.sendTab<ShowToastParam>(tabId, TabCommand.showToast, { ... })  // 同上
}
  • Ipc.getActiveTabId()undefined を返す可能性があり(background_script.tsgetCurrentTab()undefined を返すケース)、型的に number を要求する Ipc.sendTabundefined が渡され得ます
  • catch ブロック内でさらに例外が発生すると、"クリップボード取得失敗時も処理を継続する" という設計意図が崩れます
  • catch ブロック内の IPC 呼び出しも try-catch で保護することを推奨します

🟡 [中程度] chrome.sidePanel.setOptions の失敗がサイレント

packages/extension/src/services/chrome.ts:693

chrome.sidePanel.setOptions({ tabId, enabled: true })  // awaitなし

await なしで呼ばれているため、setOptions の失敗は完全にサイレントになっています。chrome.sidePanel.open 失敗時でも setOptions の副作用(タブへのオプションセット)が残ります。既存の動作と同じですが、意図的かどうかコメントで明示することを推奨します。

🟡 [軽微] 同一ロジックの重複

packages/extension/src/action/sidePanel.ts:21packages/extension/src/lib/utils.ts:70

// sidePanel.ts
if (useClipboard && isEmpty(selectionText)) {
  clipboardText = await navigator.clipboard.readText()
}

// utils.ts (toUrl内)
if (useClipboard && isEmpty(text)) {
  text = clipboardText ?? ""
}

事前チェックと toUrl() 内部に同一ロジックがあります。将来どちらかが変更された際に不整合が生じるリスクがあります。


セキュリティ上の考慮点

🟡 バックグラウンドコンテキストでの navigator.clipboard 非対応

packages/extension/src/action/sidePanel.ts:22

clipboardText = await navigator.clipboard.readText()

SidePanel.execute() がバックグラウンドコンテキスト(enableSendTab = false のとき)から呼ばれる場合、navigator.clipboard が存在しないため catch に入り、クリップボードは常に空文字列になります。フォールバックとして機能しているものの、この動作についてコメントで明示することを推奨します。


テストカバレッジ

この PR にはテスト変更が含まれていません。現在 sidePanel.test.ts は存在せず、修正された経路が完全にテストされていない状態です。以下のケースのテスト追加を推奨します:

  1. sidePanel.ts:21-23: useClipboard=true かつ selectionText が空の場合に navigator.clipboard.readText() が呼ばれること
  2. sidePanel.ts:24-33: クリップボード読み取り失敗時に console.warn が出力され、トースト通知が送信されること、かつ後続処理が継続されること
  3. sidePanel.ts:36-44: clipboardTexttoUrl() に正しく渡されること
  4. background_script.tsonCommand ハンドラー: chrome.tabs.query がリファクタリング後も正しい位置で実行されること(リグレッションテスト)

良い点

  • background_script.ts:609-614: chrome.tabs.query の実行タイミングを早期 return の後に移動した最適化は正しいアプローチです
  • sidePanel.ts:27-32: クリップボード取得失敗時にトースト通知でユーザーにフィードバックする UX は一貫性があります
  • services/chrome.ts:693-695: 冗長な変数 targetTabId を削除してコードを簡潔にした点は良い改善です

総評

バグの原因特定と修正方針は正確で、エラーハンドリングの追加も適切です。主な懸念点は catch ブロック内の IPC 呼び出しが二次例外を引き起こす可能性(上記の重要項目)です。テストが存在しない点は今後のリグレッション防止の観点から改善が望ましいですが、全体としては品質の高いPRです。

このレビューは Claude Code によって自動生成されました。

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.

2 participants