Skip to content

リンクプレビューの開き方にSidePanel選択肢を追加#299

Merged
ujiro99 merged 17 commits intomainfrom
copilot/add-sidepanel-link-preview
Mar 3, 2026
Merged

リンクプレビューの開き方にSidePanel選択肢を追加#299
ujiro99 merged 17 commits intomainfrom
copilot/add-sidepanel-link-preview

Conversation

Copy link
Contributor

Copilot AI commented Feb 27, 2026

  • DRAG_OPEN_MODEPREVIEW_SIDE_PANELを追加(const.ts
  • linkPreview.tsでSidePanelモードに対応
  • SettingForm.tsxsidePanelAutoHideトグルを追加
  • 全14言語ロケールに翻訳追加
  • Fix: migrate()originalVersionを使い、中間バージョン更新による後続マイグレーションのスキップを防止(settings.ts
  • Refactor: data.settingVersion = VERSION を各if節からmigrate()末尾の1行に集約(settings.ts
  • Fix: sidePanelClosed()内のBgData.update()awaitするよう修正(helper.ts
  • Fix: BgDataコンストラクタで旧形式のnumber[]SidePanelTab[]に正規化し後方互換を確保(backgroundData.ts
  • Add: useSidePanelAutoClose.test.tsにテストを8件追加
  • Fix: SwitchField.tsxuseRefuseState + callback refに変更(初期表示でツールチップが機能しないバグ修正)
  • Fix: chrome.tscloseSidePanelsetOptions({ enabled: false })を復元(閉鎖後もパネルが再度開けてしまう問題を修正)
  • Refactor: useSidePanelAutoClose.ts — 2つのuseEffectを1つに統合(余分なレンダリングサイクル削減)
  • Fix: helper.tscloseSidePanelのPromiseに.catch()エラーハンドラー追加(応答タイムアウト防止)
Original prompt

This section details on the original issue you should resolve

<issue_title>リンクプレビューの開き方の選択肢に、sidePanelを追加する</issue_title>
<issue_description>- [ ] リンクプレビューの開き方の選択肢として、Popup, Windowに加えてSidePanelを追加する

  • SidePaneでプレビューした場合は、メインのパネルをクリックしたらプレビューを非表示にする
  • この自動でサイドパネルを閉じる動作は、設定により無効化できる</issue_description>

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


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits February 27, 2026 23:26
Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
… keys

Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
Copilot AI changed the title [WIP] Add sidePanel option for link preview display リンクプレビューの開き方にSidePanel選択肢を追加 Feb 27, 2026
# Conflicts:
#	packages/extension/src/components/option/SettingForm.tsx
@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

❌ Patch coverage is 63.67925% with 77 lines in your changes missing coverage. Please review.
✅ Project coverage is 19.39%. Comparing base (9f3f13e) to head (224e69c).
⚠️ Report is 27 commits behind head on main.

Files with missing lines Patch % Lines
...ension/src/components/option/field/SwitchField.tsx 0.00% 23 Missing ⚠️
...es/extension/src/components/option/SettingForm.tsx 0.00% 19 Missing ⚠️
packages/extension/src/background_script.ts 57.50% 17 Missing ⚠️
packages/extension/src/services/backgroundData.ts 0.00% 6 Missing ⚠️
packages/extension/src/services/chrome.ts 16.66% 5 Missing ⚠️
...ckages/extension/src/services/settings/settings.ts 78.94% 4 Missing ⚠️
packages/extension/src/components/Popup.tsx 0.00% 2 Missing ⚠️
packages/extension/src/action/helper.ts 97.82% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #299      +/-   ##
==========================================
+ Coverage   19.03%   19.39%   +0.36%     
==========================================
  Files         312      313       +1     
  Lines       28845    28991     +146     
  Branches     1322     1372      +50     
==========================================
+ Hits         5490     5624     +134     
- Misses      23355    23367      +12     

☔ 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 marked this pull request as ready for review March 1, 2026 13:08
@ujiro99 ujiro99 requested a review from Copilot March 1, 2026 13:14
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」モードを追加し、SidePanel 表示時の自動非表示(オートクローズ)挙動を設定画面から制御できるようにする変更です。既存の SidePanel IPC を活用しつつ、タブ追跡データ(BgData)に「リンクプレビュー由来かどうか」を持たせて設定分岐できるようにしています。

Changes:

  • リンクプレビューの新規モード PREVIEW_SIDE_PANEL を追加し、選択時に SidePanel を開く処理を実装
  • SidePanel の自動非表示設定(通常コマンド用 / リンクプレビュー用)を UI・設定型・デフォルト・移行に追加
  • SidePanel のタブ追跡を拡張(tabId に加えて isLinkCommand を保持)し、onClosed 等でクリーンアップ

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/extension/src/types/index.ts LinkCommand 設定に sidePanelAutoHide を追加
packages/extension/src/test/setup.ts chrome.sidePanel.onClosed のテスト用モックを追加
packages/extension/src/services/sidePanelDetector.ts sidePanelTabs の新型に追従(some で判定)
packages/extension/src/services/sidePanelDetector.test.ts sidePanelTabs の新型に合わせてテスト更新
packages/extension/src/services/settings/settings.ts 0.15.0 向け移行(linkCommand.sidePanelAutoHide)を追加
packages/extension/src/services/option/defaultSettings.ts sidePanelAutoHide のデフォルト値を追加
packages/extension/src/services/chrome.ts OpenSidePanelPropsisLinkCommand を追加 / close の後処理を整理
packages/extension/src/services/backgroundData.ts sidePanelTabs をオブジェクト配列化(isLinkCommand を保持)
packages/extension/src/hooks/useSidePanelAutoClose.ts SidePanel 表示中のクリックで自動クローズする hook を新規追加
packages/extension/src/hooks/useSettings.test.tsx LinkCommand 設定のモックに sidePanelAutoHide を追加
packages/extension/src/const.ts DRAG_OPEN_MODE.PREVIEW_SIDE_PANEL を追加
packages/extension/src/components/option/field/SwitchField.tsx SwitchField に tooltip 表示を追加
packages/extension/src/components/option/SettingForm.tsx SidePanel 自動非表示の設定 UI を追加(link/window)
packages/extension/src/components/Popup.tsx useSidePanelAutoClose を組み込み
packages/extension/src/background_script.ts SidePanel close イベント等で追跡データをクリーンアップ
packages/extension/src/action/linkPreview.ts LinkPreview の SidePanel オープン分岐を追加(isLinkCommand: true
packages/extension/src/action/helper.ts SidePanel の open/close とクリーンアップを isLinkCommand 対応で拡張
packages/extension/src/action/helper.test.ts sidePanelTabs 新型に合わせてテスト更新
packages/extension/public/_locales/zh_CN/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/ru/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/pt_PT/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/pt_BR/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/ms/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/ko/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/ja/messages.json SidePanel 新規UI/モード文言を追加(既存表記も調整)
packages/extension/public/_locales/it/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/id/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/hi/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/fr/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/es/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/en/messages.json SidePanel 新規UI/モード文言を追加
packages/extension/public/_locales/de/messages.json SidePanel 新規UI/モード文言を追加

… add useSidePanelAutoClose tests

Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 2, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

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.1/x64/bin/node /opt/hostedtoolcache/node/24.13.1/x64/bin/node --conditions node --conditions development /home/REDACTED/work/selection-command/selection-command/node_modules/tinypool/dist/entry/process.js a (dns block)

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

Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
@claude
Copy link

claude bot commented Mar 2, 2026

PRレビュー

このPR(リンクプレビューのSidePanel選択肢追加)をレビューしました。全体的によく設計されており、特に設定マイグレーションの修正と後方互換性の実装は評価できます。以下に気になる点を記載します。


🔴 重要な問題

1. SwitchField.tsxuseRef によるツールチップ実装のバグ

ファイル: packages/extension/src/components/option/field/SwitchField.tsx:32

const span = useRef<HTMLSpanElement>(null)
// ...
<Tooltip positionElm={span.current} ... />

useRef.current プロパティはReactのレンダリングサイクルで追跡されません。初回レンダリング時に span.currentnull であり、DOMマウント後に current が更新されても Reactは再レンダリングをトリガーしません。そのため TooltippositionElm={null} のまま初期化され、フォームが別の理由で再レンダリングされるまでツールチップが表示されないリスクがあります。

修正案:

const [spanEl, setSpanEl] = useState<HTMLSpanElement | null>(null)
// ...
<span ref={setSpanEl} ...>
// ...
<Tooltip positionElm={spanEl} ... />

useState + callback ref を使うと、DOMマウント時に確実に再レンダリングが発生し、ツールチップのイベントリスナーが正しく登録されます。


2. chrome.tssetOptions({ enabled: false }) の削除

ファイル: packages/extension/src/services/chrome.ts:674

元のコードでは closeSidePanel 内に以下の処理がありました(削除された箇所):

await chrome.sidePanel.setOptions({ tabId, enabled: false })

このコードはサイドパネルを閉じた後にChromeのUI上でも無効化する役割を果たしていました。削除されたことで、プログラム的にサイドパネルを閉じた後も、ユーザーがChromeのツールバーから手動でサイドパネルを再度開けるようになっています。

意図的な変更であれば問題ありませんが、元の動作との差異として記録しておく価値があります。chrome.sidePanel.onClosedsidePanelClosed() が呼ばれ BgData の更新は行われますが、パネルの「enabled」状態は変わりません。


🟡 中程度の問題

3. useSidePanelAutoClose.ts — 2つの useEffect による余分なレンダリングサイクル

ファイル: packages/extension/src/hooks/useSidePanelAutoClose.ts:12

BgDataの変化(Effect 1)→ state更新 → 再レンダリング → clickリスナー登録(Effect 2)という流れになっており、BgData更新からclickリスナー登録まで1レンダリングサイクルの遅延があります。実際には問題になりにくいですが、設計としては以下のようにBgDataと設定を組み合わせて単一のeffectに統合することも検討できます。

useEffect(() => {
  const check = (data: BgData) => {
    const tab = data.sidePanelTabs.find((t) => t.tabId === tabId)
    if (!tab) return
    const autoHideEnabled = tab.isLinkCommand
      ? userSettings?.linkCommand?.sidePanelAutoHide
      : userSettings?.windowOption?.sidePanelAutoHide
    if (!autoHideEnabled) return
    const close = () => Ipc.send(BgCommand.closeSidePanel)
    window.addEventListener("click", close)
    return () => window.removeEventListener("click", close)
  }
  // ...
}, [tabId, userSettings])

4. helper.tscloseSidePanel のPromiseエラーハンドリング

ファイル: packages/extension/src/action/helper.ts:161

enhancedSettings.get().then(async (settings) => {
  // ...
  await _closeSidePanel(tabId)  // ここで例外が発生した場合
  response(true)
})

.then() チェーンにエラーハンドラーがありません。_closeSidePanel が例外を投げた場合、response(true) が呼ばれず、Chrome拡張のメッセージングで応答タイムアウトが発生する可能性があります。.catch(err => { console.warn(...); response(false) }) の追加を検討してください。


🟢 良好な実装

5. settings.ts — マイグレーション連鎖バグの修正 ✅

ファイル: packages/extension/src/services/settings/settings.ts:224

const originalVersion = data.settingVersion
if (versionDiff(originalVersion, "0.10.0") === VersionDiff.Old) { ... }
if (versionDiff(originalVersion, "0.10.3") === VersionDiff.Old) { ... }
// ...
data.settingVersion = VERSION as Version

各マイグレーション内で settingVersion を更新していた問題を originalVersion を使って正しく修正しています。また、settingVersion の更新を末尾の1行に集約した点も保守性が向上しています。

6. backgroundData.ts — 後方互換性の処理 ✅

ファイル: packages/extension/src/services/backgroundData.ts:30

this.sidePanelTabs = (val?.sidePanelTabs ?? []).map((t) =>
  typeof t === "number" ? { tabId: t, isLinkCommand: false } : t,
)

number[] から SidePanelTab[] への型変更に際して、旧形式データの正規化が適切に実装されています。

7. background_script.ts — サイドパネルイベントハンドリングの改善 ✅

ファイル: packages/extension/src/background_script.ts:661

chrome.sidePanel.onClosed.addListener(({ tabId }) =>
  ActionHelper.sidePanelClosed(tabId),
)

chrome.sidePanel.onClosed を使うことでサイドパネルが閉じられた際のクリーンアップが確実に行われるようになっています。タブ削除(chrome.tabs.onRemoved)のみに依存していた従来の方法より堅牢です。


🔵 軽微な指摘

8. analytics.ts — 変数の冗長性

ファイル: packages/extension/src/services/analytics.ts:12

const IS_CI = typeof process !== "undefined" && process.env && process.env.CI === "true"
const DISABLE_ANALYTICS = IS_CI  // IS_CIのエイリアスであり不要

DISABLE_ANALYTICSIS_CI の単純なエイリアスであり、独立した変数として定義する必要はありません。直接 IS_CI を使用するか、IS_CI を削除して DISABLE_ANALYTICS のみ使用する方が明確です。

9. chrome.ts — フォーマット変更

ファイル: packages/extension/src/services/chrome.ts:335

-chrome.runtime.onConnect.addListener(function (port) {
+chrome.runtime.onConnect.addListener(function(port) {

Prettierの設定(spaceBeforeFunctionParen)に依存しますが、関数名とカッコの間のスペース削除が意図的な変更かどうか確認してください。他の箇所のスタイルと統一されているか確認をお勧めします。


まとめ

カテゴリ 件数
🔴 重要 2件
🟡 中程度 2件
🟢 良好 3件
🔵 軽微 2件

最も注意が必要なのは SwitchField.tsxuseRef によるツールチップバグです。フォームが初期表示されたタイミングでツールチップアイコンにホバーしても表示されない可能性があります。useState + callback ref への変更を推奨します。

@ujiro99
Copy link
Owner

ujiro99 commented Mar 2, 2026

PRレビュー

このPR(リンクプレビューのSidePanel選択肢追加)をレビューしました。全体的によく設計されており、特に設定マイグレーションの修正と後方互換性の実装は評価できます。以下に気になる点を記載します。


🔴 重要な問題

1. SwitchField.tsxuseRef によるツールチップ実装のバグ

ファイル: packages/extension/src/components/option/field/SwitchField.tsx:32

const span = useRef<HTMLSpanElement>(null)
// ...
<Tooltip positionElm={span.current} ... />

useRef.current プロパティはReactのレンダリングサイクルで追跡されません。初回レンダリング時に span.currentnull であり、DOMマウント後に current が更新されても Reactは再レンダリングをトリガーしません。そのため TooltippositionElm={null} のまま初期化され、フォームが別の理由で再レンダリングされるまでツールチップが表示されないリスクがあります。

修正案:

const [spanEl, setSpanEl] = useState<HTMLSpanElement | null>(null)
// ...
<span ref={setSpanEl} ...>
// ...
<Tooltip positionElm={spanEl} ... />

useState + callback ref を使うと、DOMマウント時に確実に再レンダリングが発生し、ツールチップのイベントリスナーが正しく登録されます。


2. chrome.tssetOptions({ enabled: false }) の削除

ファイル: packages/extension/src/services/chrome.ts:674

元のコードでは closeSidePanel 内に以下の処理がありました(削除された箇所):

await chrome.sidePanel.setOptions({ tabId, enabled: false })

このコードはサイドパネルを閉じた後にChromeのUI上でも無効化する役割を果たしていました。削除されたことで、プログラム的にサイドパネルを閉じた後も、ユーザーがChromeのツールバーから手動でサイドパネルを再度開けるようになっています。

意図的な変更であれば問題ありませんが、元の動作との差異として記録しておく価値があります。chrome.sidePanel.onClosedsidePanelClosed() が呼ばれ BgData の更新は行われますが、パネルの「enabled」状態は変わりません。


🟡 中程度の問題

3. useSidePanelAutoClose.ts — 2つの useEffect による余分なレンダリングサイクル

ファイル: packages/extension/src/hooks/useSidePanelAutoClose.ts:12

BgDataの変化(Effect 1)→ state更新 → 再レンダリング → clickリスナー登録(Effect 2)という流れになっており、BgData更新からclickリスナー登録まで1レンダリングサイクルの遅延があります。実際には問題になりにくいですが、設計としては以下のようにBgDataと設定を組み合わせて単一のeffectに統合することも検討できます。

useEffect(() => {
  const check = (data: BgData) => {
    const tab = data.sidePanelTabs.find((t) => t.tabId === tabId)
    if (!tab) return
    const autoHideEnabled = tab.isLinkCommand
      ? userSettings?.linkCommand?.sidePanelAutoHide
      : userSettings?.windowOption?.sidePanelAutoHide
    if (!autoHideEnabled) return
    const close = () => Ipc.send(BgCommand.closeSidePanel)
    window.addEventListener("click", close)
    return () => window.removeEventListener("click", close)
  }
  // ...
}, [tabId, userSettings])

4. helper.tscloseSidePanel のPromiseエラーハンドリング

ファイル: packages/extension/src/action/helper.ts:161

enhancedSettings.get().then(async (settings) => {
  // ...
  await _closeSidePanel(tabId)  // ここで例外が発生した場合
  response(true)
})

.then() チェーンにエラーハンドラーがありません。_closeSidePanel が例外を投げた場合、response(true) が呼ばれず、Chrome拡張のメッセージングで応答タイムアウトが発生する可能性があります。.catch(err => { console.warn(...); response(false) }) の追加を検討してください。


🟢 良好な実装

5. settings.ts — マイグレーション連鎖バグの修正 ✅

ファイル: packages/extension/src/services/settings/settings.ts:224

const originalVersion = data.settingVersion
if (versionDiff(originalVersion, "0.10.0") === VersionDiff.Old) { ... }
if (versionDiff(originalVersion, "0.10.3") === VersionDiff.Old) { ... }
// ...
data.settingVersion = VERSION as Version

各マイグレーション内で settingVersion を更新していた問題を originalVersion を使って正しく修正しています。また、settingVersion の更新を末尾の1行に集約した点も保守性が向上しています。

6. backgroundData.ts — 後方互換性の処理 ✅

ファイル: packages/extension/src/services/backgroundData.ts:30

this.sidePanelTabs = (val?.sidePanelTabs ?? []).map((t) =>
  typeof t === "number" ? { tabId: t, isLinkCommand: false } : t,
)

number[] から SidePanelTab[] への型変更に際して、旧形式データの正規化が適切に実装されています。

7. background_script.ts — サイドパネルイベントハンドリングの改善 ✅

ファイル: packages/extension/src/background_script.ts:661

chrome.sidePanel.onClosed.addListener(({ tabId }) =>
  ActionHelper.sidePanelClosed(tabId),
)

chrome.sidePanel.onClosed を使うことでサイドパネルが閉じられた際のクリーンアップが確実に行われるようになっています。タブ削除(chrome.tabs.onRemoved)のみに依存していた従来の方法より堅牢です。


🔵 軽微な指摘

8. analytics.ts — 変数の冗長性

ファイル: packages/extension/src/services/analytics.ts:12

const IS_CI = typeof process !== "undefined" && process.env && process.env.CI === "true"
const DISABLE_ANALYTICS = IS_CI  // IS_CIのエイリアスであり不要

DISABLE_ANALYTICSIS_CI の単純なエイリアスであり、独立した変数として定義する必要はありません。直接 IS_CI を使用するか、IS_CI を削除して DISABLE_ANALYTICS のみ使用する方が明確です。

9. chrome.ts — フォーマット変更

ファイル: packages/extension/src/services/chrome.ts:335

-chrome.runtime.onConnect.addListener(function (port) {
+chrome.runtime.onConnect.addListener(function(port) {

Prettierの設定(spaceBeforeFunctionParen)に依存しますが、関数名とカッコの間のスペース削除が意図的な変更かどうか確認してください。他の箇所のスタイルと統一されているか確認をお勧めします。


まとめ

カテゴリ 件数
🔴 重要 2件
🟡 中程度 2件
🟢 良好 3件
🔵 軽微 2件

最も注意が必要なのは SwitchField.tsxuseRef によるツールチップバグです。フォームが初期表示されたタイミングでツールチップアイコンにホバーしても表示されない可能性があります。useState + callback ref への変更を推奨します。

@copilot
重要な問題2点と、中程度の問題2点を修正してください。

…catch handler

Co-authored-by: ujiro99 <677231+ujiro99@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 2, 2026

PRレビュー

このPR(リンクプレビューのSidePanel選択肢追加)をレビューしました。全体的によく設計されており、特に設定マイグレーション��...

レビューいただいた重要2点・中程度2点を修正しました(commit 2e05880)。

  1. SwitchField.tsxuseRefuseState + callback ref に変更。setSpanElref として渡すことで DOMマウント時に再レンダリングが発生し、ツールチップの positionElm に正しく DOM 要素が渡されるようになりました。
  2. chrome.tscloseSidePanelawait chrome.sidePanel.setOptions({ tabId, enabled: false }) を復元。プログラム的に閉じた後も Chrome UI 上でパネルが有効なままになる問題を修正しました。
  3. useSidePanelAutoClose.ts — 2つの useEffect を1つに統合。cleanupClickListener で前のリスナーを管理し、BgData 変化 → state更新 → 再レンダリング の余分なサイクルをなくしました。
  4. helper.ts.catch(err => { console.warn(...); response(false) }) を追加し、_closeSidePanel で例外が発生しても応答タイムアウトが起きないようにしました。

@claude
Copy link

claude bot commented Mar 3, 2026

PR レビュー: リンクプレビューの開き方にSidePanel選択肢を追加

全体的に機能の目的は明確で、後方互換性への配慮(number[]SidePanelTab[]の正規化、originalVersion修正)や、テストの追加なども評価できます。以下、気になった点を挙げます。


🔴 重大な問題

1. sleep(1000) のハードコード

ファイル: packages/extension/src/services/chrome.ts:677

await sleep(1000) // Wait for the side panel to close before disabling it

closeSidePanelに1秒のsleepが追加されています。これには2つの問題があります。

  • UXの低下: パネルを閉じるたびに1秒の遅延が生じる
  • レースコンディション: ユーザーが1秒以内にパネルを再度開いた場合、setOptions({ enabled: false })が再開後に実行されてしまい、意図せずパネルが無効化される可能性がある

chrome.sidePanel.onClosedイベントで後処理を行うようになったため、このsleepが本当に必要かを再検討してください。必要であれば、定数化してコメントで理由を明記することを推奨します。


🟠 バグの可能性

2. migrate() における originalVersion の適用漏れリスク

ファイル: packages/extension/src/services/settings/settings.ts:222-254

originalVersionを使ってマイグレーションの条件を判定する修正は正しい方向性ですが、各マイグレーション関数(例: migrate0_10_0)の内部でdataを上書きする際、後続のマイグレーションが前段の変更を前提としていると問題になる可能性があります。

現在の各マイグレーション関数が冪等(idempotent)に設計されているかを確認し、必要であればコメントで明示してください。

3. sidePanelClosedtabId ガード

ファイル: packages/extension/src/action/helper.ts:619

export const sidePanelClosed = async (tabId?: number): Promise<void> => {
  if (!tabId) return

!tabIdtabId === 0も弾いてしまいます。Chrome のタブIDが0になることは通常ありませんが、型の意図を明確にするために以下の書き方が推奨されます:

if (tabId == null) return

🟡 コード品質・設計の改善提案

4. SwitchField.tsx の Tailwind クラス名

ファイル: packages/extension/src/components/option/field/SwitchField.tsx:66

className="cursor-pointer p-1 rounded hover:bg-gray-100 transition-background"

transition-background は Tailwind CSS v3 に存在しないクラスです。正しくは transition-colors を使用してください。現状では CSS トランジションが適用されない可能性があります。

5. analytics.ts の CI 検出ロジック

ファイル: packages/extension/src/services/analytics.ts:12-14

const IS_CI =
  typeof process !== "undefined" && process.env && process.env.CI === "true"

Vite ではビルド時に import.meta.envprocess.env を静的解析・置換します。このコードはビルド時に評価されるため、実行時の CI 環境検出としては機能しません(CI環境でビルドされた場合のみ true になる)。

テスト環境でのみ analytics を無効化したいなら、テスト用の setup.ts でモックする方が適切です。また、既に !MEASUREMENT_ID || !API_SECRET のチェックがあるため、この条件で十分かもしれません。

6. openSidePanel における if (tabId) の冗長チェック

ファイル: packages/extension/src/action/helper.ts:131-136

let tabId = sender.tab?.id
if (tabId == null) {
  // ... fallbackで代入
}
// ...
.then(() => {
  if (tabId) {  // ← この時点でtabIdはnullでないことが保証されているが型推論が追いつかない

TypeScript の型推論がクロージャを超えられないため二重チェックが必要なことは理解できますが、コメントを追加して意図を明示してください。


🔵 テストカバレッジ

Codecov によると以下のファイルのカバレッジが低い状態です:

ファイル カバレッジ
packages/extension/src/action/helper.ts 7.69%(24行未カバー)
packages/extension/src/action/linkPreview.ts 0.00%(9行未カバー)

特に以下の新しいコードパスのテストが不足しています:

  • helper.ts:109-557 - openSidePanel での bgData.activeTabId へのフォールバック
  • helper.ts:160-207 - closeSidePanelisLinkCommand 分岐(両設定値の組み合わせ)
  • linkPreview.ts:661-668 - PREVIEW_SIDE_PANEL モードの実行パス
  • helper.ts:618-631 - sidePanelClosed 関数本体

✅ 良い点

  • backgroundData.ts:30-33 - レガシーの number[] から SidePanelTab[] への後方互換変換は適切
  • settings.ts - originalVersion を使用したマイグレーション修正は重要なバグ修正
  • chrome.ts - setOptions({ enabled: false }) の復元によりパネルが意図せず再表示される問題を解決
  • SwitchField.tsx - useRef から useCallback ref への変更は初期レンダリング時のツールチップ位置問題を正しく修正
  • useSidePanelAutoClose.test.ts - 8件のテストケースが適切なパターン(SPAC-01〜08)で整理されている
  • 14言語への翻訳追加 - 全ロケールに対して一貫して適用されている

以上です。全体的に実装の方向性は良く、細かい改善点を修正すればマージ可能な品質だと思います。

@claude
Copy link

claude bot commented Mar 3, 2026

PR レビュー: リンクプレビューの開き方に SidePanel 選択肢を追加 (#299)

全体的に、機能追加とバグ修正がよくまとめられた PR です。以下に気になる点をまとめます。


✅ 良い点

settings.tsmigrate() のバグ修正
currentVersion = data.settingVersion を冒頭でキャプチャして、変換中に data.settingVersion が書き換わっても後続のマイグレーションが正しく適用される修正は的確です。旧コードでは中間で data.settingVersion = VERSION がセットされ、以降のバージョン比較が常に「新しい」と判断されてスキップされるバグがありました。

後方互換の対応 (backgroundData.ts)
sidePanelTabsnumber[] から SidePanelTab[] へ変換する際、コンストラクタ内で旧形式をノーマライズしている点は安全です。


🐛 バグ・潜在的問題

1. windowOption.sidePanelAutoHide が常時表示される

packages/extension/src/components/option/SettingForm.tsx L627–633

linkCommand.sidePanelAutoHideopenMode === PREVIEW_SIDE_PANEL のときだけ表示されますが、windowOption.sidePanelAutoHide は openMode に関わらず常に表示されています。windowOption 側にも同様の条件分岐を追加すべきではないでしょうか。

// linkCommand は条件付き (OK)
{linkCommandOpenMode === DRAG_OPEN_MODE.PREVIEW_SIDE_PANEL && (
  <SwitchField ... name="linkCommand.sidePanelAutoHide" />
)}

// windowOption は常時表示 (意図的?)
<SwitchField ... name="windowOption.sidePanelAutoHide" />

windowOptionsidePanelAutoHide は、コマンド系(サイドパネルで開くコマンド)全体に適用される設定なので、あえて常時表示という設計もあり得ますが、意図を明確にするコメントがあると良いと思います。

2. chrome.ts — ハードコードされた 1 秒の待機

packages/extension/src/services/chrome.ts L689 付近

const SIDE_PANEL_CLOSE_ANIMATION = 1000
// ...
await sleep(SIDE_PANEL_CLOSE_ANIMATION)

アニメーション待ちに 1 秒のハードコード値は、パネルをすぐに再オープンしようとした場合(例:別のリンクを連続クリック)に 1 秒待たされる UX の問題があります。Chrome 側のアニメーション仕様変更でもズレるリスクがあります。定数に根拠をコメントで記載するか、より短い値を検討してください。

3. helper.tsif (tabId) のチェックが冗長

packages/extension/src/action/helper.ts L131

.then(() => {
  // Register the tab ID for tracking
  if (tabId) {   // ← ここより上で tabId が null のケースは return 済み
    const newEntry = { tabId, isLinkCommand: param.isLinkCommand ?? false }

この時点で tabId は非 null が保証されているので、if (tabId) は不要です(TypeScript の型推論でも number として扱われているはず)。コードの意図が曖昧になるため削除を推奨します。


⚠️ パフォーマンス・品質上の懸念

4. テストでの setTimeout(resolve, 10) 使用

packages/extension/src/action/helper.test.ts L622, L648, L702, L761 等

await new Promise((resolve) => setTimeout(resolve, 10))

このパターンは時間依存で CI 環境によっては不安定(flaky)になります。Vitest の vi.runAllTimersAsync()vi.flushPromises() を使う方がより信頼性が高くなります。

5. useSidePanelAutoClose.tsuserSettings 変更ごとにリスナーが再登録される

packages/extension/src/hooks/useSidePanelAutoClose.ts L41

}, [tabId, userSettings])

useEffect の依存配列に userSettings オブジェクト全体が入っているため、設定が変わるたびにクリックリスナーが削除→再追加されます。userSettings?.windowOption?.sidePanelAutoHideuserSettings?.linkCommand?.sidePanelAutoHide など具体的な値のみを依存配列に含めることで、不要な再登録を避けられます。


📝 スタイル・コード品質

6. chrome.ts でのフォーマット変更が不一致

packages/extension/src/services/chrome.ts L332–337

-      chrome.runtime.onConnect.addListener(function (port) {
+      chrome.runtime.onConnect.addListener(function(port) {

この変更はプロジェクト全体のスタイルと一致していない可能性があります(他の箇所では function (port) の形式を維持)。意図的なフォーマット変更であれば、プロジェクト全体に統一すべきです。


🔍 テストカバレッジについて


まとめ

重要度 件数
🐛 バグ・問題 3 件
⚠️ 品質改善 2 件
📝 スタイル 1 件

特に windowOption.sidePanelAutoHide の常時表示(問題 #1)と、1 秒ハードコード(問題 #2)は UX/動作に影響するため、マージ前に確認いただけると良いと思います。

@ujiro99 ujiro99 merged commit a57390e into main Mar 3, 2026
6 checks passed
@ujiro99 ujiro99 deleted the copilot/add-sidepanel-link-preview branch March 3, 2026 04:29
@claude
Copy link

claude bot commented Mar 3, 2026

PR レビュー: リンクプレビューの開き方にSidePanel選択肢を追加

全体的に高品質な実装で、バグ修正・リファクタリング・テスト追加が適切に行われています。以下にいくつかの指摘事項を記します。


🔴 重要な問題

1. chrome.tscloseSidePanel の後処理がイベント依存になっている

ファイル: packages/extension/src/services/chrome.tscloseSidePanel 関数 / SIDE_PANEL_CLOSE_ANIMATION 付近)

変更後、BgDatasidePanelTabs / sidePanelUrls)のクリーンアップが chrome.sidePanel.onClosed イベントの発火に完全依存しています。このイベントが確実に発火しないシナリオ(例: サービスワーカーの再起動、拡張機能のリロード直後)では sidePanelTabs にゴミデータが残り続ける可能性があります。

closeSidePanel 関数の中でも sidePanelClosed() を呼ぶ二重クリーンアップ(sidePanelClosed は冪等なので安全)を検討してください。


2. background_script.tschrome.tabs.onRemovedchrome.sidePanel.onClosed の重複呼び出し

ファイル: packages/extension/src/background_script.ts(633〜644行付近)

タブが削除されたとき、chrome.tabs.onRemovedchrome.sidePanel.onClosed の両方が発火し、ActionHelper.sidePanelClosedBgData.update が2回呼ばれる可能性があります。現在の実装は冪等なので壊れませんが、不要なストレージ書き込みが発生します。chrome.tabs.onRemoved 側では sidePanelClosed を呼ばず updateActiveTabId のみにする設計も検討できます。


🟡 注意点・改善提案

3. settings.ts — マイグレーション修正の意図をコメントで明記することを推奨

ファイル: packages/extension/src/services/settings/settings.ts(222〜225行)

currentVersion スナップショットを使う修正は正しいです。今後の開発者が新しいマイグレーションを追加する際に「currentVersion を使う」という慣例を忘れやすいため、関数冒頭への説明コメントの追加を推奨します。

4. chrome.ts — マジックナンバー SIDE_PANEL_CLOSE_ANIMATION = 1000

ファイル: packages/extension/src/services/chrome.ts(663行付近)

1000ms はアニメーション完了待ちのためのものですが、Chrome のバージョンや OS によって実際のアニメーション時間が異なる場合があります。helper.test.ts でこの sleep がテスト時にモック(vi.useFakeTimers() 等)されているか確認が必要です。また「この値は Chrome サイドパネルのクローズアニメーション時間に基づく」等のコメントを追加することを推奨します。

5. SettingForm.tsxsidePanelAutoHide_link が非表示時も値が保持される

ファイル: packages/extension/src/components/option/SettingForm.tsx(597〜604行)

ユーザーが一度 PREVIEW_SIDE_PANEL を選択して sidePanelAutoHidetrue にした後、openModePREVIEW_POPUP 等に変更してもフォーム上の値は true のまま保存されます。PREVIEW_SIDE_PANEL 以外のモードでは実質的に無効な設定が永続化されるため、openMode 変更時に sidePanelAutoHidefalse にリセットする処理(useEffect + setValue)の追加を検討してください。

6. helper.ts — 未使用変数 _ が ESLint 警告になる可能性

ファイル: packages/extension/src/action/helper.tssidePanelClosed 関数内)

プロジェクトの ESLint 設定によっては no-unused-vars 警告が出る場合があります。const { [tabId]: _, ...rest } = data.sidePanelUrls_ について問題がなければそのままで構いませんが、確認してください。

7. analytics.tsprocess の存在チェックが冗長な可能性

ファイル: packages/extension/src/services/analytics.ts(9〜12行)

Vite 環境では process.env はビルド時に静的置換されるため、ランタイムでの typeof process !== "undefined" チェックは不要な場合があります。import.meta.env.CI を使う方が Vite の慣例に合っています。ただし CI 環境の設定方法によっては現状の実装が必要な場合もあるため、動作確認済みであれば問題ありません。


✅ 良い点

  • settings.ts のマイグレーション修正: currentVersion スナップショット使用と settingVersion 代入の一元化は重要なバグ修正で、適切な実装です。
  • backgroundData.ts の後方互換対応: number[] から SidePanelTab[] へのコンストラクタ内での正規化は、既存ユーザーのデータを壊さない優れた設計です。
  • SwitchField.tsx の callback ref 修正: 初期レンダリング時にツールチップが表示されないバグに対する useRef から useState + callback ref への修正は適切です。
  • テストカバレッジ: LP-XXSPAC-XXOSP-XXCSP-XXSPC-XX の命名規則に従った豊富なテストが追加されており、CLAUDE.md のガイドラインに準拠しています。
  • openSidePanel のフォールバック: sender.tab?.id が null の場合に bgData.activeTabId を使うフォールバックは、リンクコマンド経由の呼び出し時の問題を適切に解決しています。

このレビューは Claude Code (claude-sonnet-4-6) によって自動生成されました。

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を追加する

3 participants