Add sidePanel open mode with focus-based auto-hide#274
Conversation
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>
プルリクエストレビューこのPRは、ChromeのサイドパネルAPIを使用してURLを開く新しい機能を追加しています。全体的な実装は良好ですが、いくつかの改善点とセキュリティ上の懸念があります。 ✅ 良い点
🔴 重大な問題1. セキュリティ上の懸念: XSS脆弱性のリスク// packages/extension/src/services/chrome.ts:611-614
chrome.sidePanel.setOptions({
tabId: targetTabId,
path: toUrl(url), // ⚠️ ユーザー入力が直接pathに使用されている
enabled: true,
})問題点: 推奨される修正:
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)
}
}問題点:
推奨される修正: 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)
}
}
|
Pull Request Review: サイドパネル機能の追加このPRでは、Chrome拡張機能にサイドパネル表示機能を追加し、自動非表示機能を実装しています。全体的に良好な実装ですが、いくつか改善すべき点を見つけました。 🟢 良い点アーキテクチャ
実装の堅牢性
🔴 重大な問題1. 競合状態のリスク (helper.ts:106-137)
現在のコード: export const openSidePanel = (
param: OpenSidePanelProps,
sender: Sender,
): boolean => {
// ...async operations...
return false // ❌ 非同期処理を開始しているのにfalseを返している
}推奨される修正: export const openSidePanel = (
param: OpenSidePanelProps,
sender: Sender,
response: (res: boolean) => void,
): boolean => {
const tabId = sender.tab?.id
_openSidePanel({ ...param, tabId })
.then(() => incrementCommandExecutionCount(tabId))
.then(() => {
if (tabId) {
return BgData.update((data) => ({
sidePanelTabs: data.sidePanelTabs.includes(tabId)
? data.sidePanelTabs
: [...data.sidePanelTabs, tabId],
}))
}
})
.then(() => response(true))
.catch((error) => {
console.error('[ActionHelper.openSidePanel] Error:', error)
response(false)
})
return true // ✅ 非同期処理を示すためtrueを返す
}2. useSidePanelAutoCloseの論理エラー (useSidePanelAutoClose.ts:6-31)このフックには複数の問題があります: a) イベントリスナーのタイミング問題サイドパネルが表示された直後にfocus/clickイベントリスナーを追加していますが、これにより即座にサイドパネルが閉じられる可能性があります。 b) ウィンドウフォーカスイベントの誤解
c) クリックイベントの不必要な使用すべてのクリックでサイドパネルを閉じるのは、UX的に問題があります。 推奨される修正: // background_script.tsに追加
chrome.windows.onFocusChanged.addListener(async (windowId: number) => {
// 既存のコード...
// Side panel auto-hide機能
const settings = await enhancedSettings.get()
if (settings.windowOption.sidePanelAutoHide) {
const [currentTab] = await chrome.tabs.query({
active: true,
windowId: windowId
})
if (currentTab?.id) {
const bgData = BgData.get()
if (bgData.sidePanelTabs.includes(currentTab.id)) {
await closeSidePanel(currentTab.id)
}
}
}
})そして 🟡 中程度の問題3. テストカバレッジの不足新しく追加された機能に対する専用のテストがありません:
推奨アクション:
4. タブURLの変更時のクリーンアップ (background_script.ts)
推奨される修正: chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
if (changeInfo.url) {
// URL変更時にsidePanelTabsからタブIDを削除
BgData.update((data) => ({
sidePanelTabs: data.sidePanelTabs.filter((id) => id \!== tabId),
}))
}
})5. chrome.sidePanel.setOptions()のエラーハンドリング不足 (chrome.ts:612-616)
推奨される修正: try {
await chrome.sidePanel.setOptions({
tabId: targetTabId,
path: toUrl(url),
enabled: true,
})
await chrome.sidePanel.open({ tabId: targetTabId })
} catch (error) {
console.error('Failed to open side panel:', error)
throw error
}🟢 軽微な改善提案6. 型定義の改善
推奨される修正: export type OpenSidePanelProps = {
url: string | UrlParam
tabId: number // オプショナルを削除
}7. マジックナンバーの定数化closeSidePanelの onClosed リスナーのクリーンアップパターンは良好ですが、コメントで意図を明確にすると良いでしょう。 8. 国際化メッセージの確認多言語対応ファイルに追加されたメッセージが適切に翻訳されているか確認してください。特に自動翻訳を使用している場合は、ネイティブスピーカーによるレビューを推奨します。 🔒 セキュリティ考慮事項✅ 問題なし: URLのサニタイズは 📊 パフォーマンス考慮事項✅ 概ね良好: BgData.updateの使用頻度は適切で、不要なストレージ操作はありません。
📝 まとめ必須の修正:
強く推奨:
このPRは有用な機能追加ですが、上記の問題を修正してからマージすることを強く推奨します。特に1と2は競合状態やUX問題を引き起こす可能性があるため、優先的に対処してください。 レビューは packages/extension 内のコードを中心に実施しました。CLAUDE.mdの規約に従い、TypeScriptのベストプラクティスと Chrome 拡張機能の Manifest V3 の制約を考慮しています。 |
Adds
OPEN_MODE.SIDE_PANELto 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 viasidePanelAutoHidesetting (default: enabled).Core Implementation
OPEN_MODE.SIDE_PANELin shared constants and extension mappingsaction/sidePanel.tswith IPC integration viaBgCommand.openSidePanelopenSidePanel()inservices/chrome.tsusingchrome.sidePanel.setOptions()andchrome.sidePanel.open()Auto-Hide Mechanism
BgData.sidePanelTabs[]chrome.windows.onFocusChangedlistener closes side panel for focused tab whensidePanelAutoHideis enabledchrome.tabs.onRemovedandchrome.tabs.onUpdatedincludes()check before adding tab IDsConfiguration
sidePanelAutoHide?: booleantoUserSettingstypetrueindefaultSettings.tsPermissions
"sidePanel"tomanifest.jsonpermissions arrayWarning
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/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)/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
💡 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.