I want to use keyboard shortcut to open side panel but I got an error: Uncaught (in promise) Error: sidePanel.open()
may only be called in response to a user gesture.
error image
{
"manifest_version": 3,
"name": "Global side panel",
"version": "1.0",
"description": "Sidepanel declared only in the manifest visible to all sites",
"icons": {
"16": "images/icon-16.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png"
},
"background": {
"service_worker": "background.js"
},
"side_panel": {
"default_path": "sidepanel.html"
},
"permissions": ["sidePanel"],
"commands": {
"Open-side-panel": {
"suggested_key": {
"default": "Ctrl+Shift+Y",
"mac": "Command+Shift+Y"
},
"description": "Open side panel"
}
}
}
chrome.commands.onCommand.addListener(async (command) => {
console.log(`Command "${command}" called`);
const [tab] = await chrome.tabs.query({active: true})
chrome.sidePanel.open({tabId:tab.id});
});
It's a bug: this API doesn't keep the gesture flag for more than ~1ms, unlike the other APIs that do it for a short amount of time up to 1 second.
The workaround in a simple case like this is using a callback instead of await:
chrome.commands.onCommand.addListener(() => {
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
chrome.sidePanel.open({ tabId: tab.id });
});
});
The workaround for a more complicated asynchronous query is to prepare tabId beforehand and make the worker script run forever in order to keep the variable:
let activeTabId;
// keep alive, see stackoverflow.com/a/66618269
setInterval(chrome.runtime.getPlatformInfo, 25e3);
chrome.runtime.onStartup.addListener(async () => {
activeTabId = (await chrome.tabs.query({ active: true, currentWindow: true }))[0]?.id;
});
chrome.tabs.onActivated.addListener(info => {
activeTabId = info.tabId;
});
chrome.commands.onCommand.addListener(() => {
chrome.sidePanel.open({tabId});
});