Allow displaying side panel only on specific pages
Feature Request
Current WXT always adds a side_panel entry to the manifest file if at least one side panel is present. That makes the side panel be displayed on every site (i.e. it doesn't matter where and when I open the extension's side panel, it will be preserved across the tab switches). I would like to display the side panel only when a certain website is displayed in a tab, when I switch to a different tab, the side panel should be hidden.
This is possible by adding some code to the background/service worker script as described here, but it doesn't work if the side_panel entry with default_path is defined in the manifest.
I'd like to have an option to remove this entry entirely from the manifest, perhaps through <meta> tag although I don't have a big preference
Is your feature request related to a bug?
N/A
What are the alternatives?
I just found out that it should be possible to remove it through hooks, but I haven't checked it yet. In any case I think it would be nice to have a cleaner option for doing so.
Additional context
N/A
I was not familiar with this use-case... It's a good idea. The boilerplate to set this up, while possible today, is quite annoying:
- Create a HTML entrypoint NOT named "sidepanel" (ex:
entrypoints/<your-website>-sidepanel.html) - Add the
tabsandsidePanelpermissions to your manifest, as well as a blankactionobject:import { defineConfig } from "wxt"; // See https://wxt.dev/api/config.html export default defineConfig({ extensionApi: "chrome", manifest: { action: {}, permissions: ["tabs", "sidePanel"], }, }); - Add the following code to your background:
const SIDEPANEL_MATCH_PATTERN = new MatchPattern("*://*.wxt.dev/*"); export default defineBackground(() => { browser.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }); browser.tabs.onUpdated.addListener(async (tabId, info, tab) => { if (!tab.url) return; if (SIDEPANEL_MATCH_PATTERN.includes(tab.url)) { // Enables the side panel on google.com await browser.sidePanel.setOptions({ tabId, path: "wxt-sidepanel.html", enabled: true, }); } else { // Disables the side panel on all other sites await browser.sidePanel.setOptions({ tabId, enabled: false, }); } }); });
Here's the full example: website-specific-sidepanel-example.zip
Thinking about how to implement this... It would be really nice if you could just provide the match patterns in a meta field, then WXT would automatically setup all that code in the background for you:
<meta name="sidepanel.matchPatterns" value="['*://*.wxt.dev/*']"></meta>
But we need to consider all the use-cases.
- Only one sidepanel using
side_panel.default_path - Different sidepanels for different websites
-
side_panel.default_path, but change it at runtime - Open sidepanel when clicking action
Any other use-cases I'm missing? I'm basically just copying from Chrome's docs... Only other case I can think of is combining options 1 and 2. Providing both a default path AND additional sidepanels with match patterns.
So let's think about the ideal way to implment these:
-
Only one sidepanel using
side_panel.default_path: As it is today, just adding a sidepanel entrypoint does this. - Different sidepanels for different websites: What I recommended above with setting up match patterns would be useful. Need to think about how to support multiple side panels and merge the JS logic together.
-
side_panel.default_path, but change it at runtime: As it is today is fine, changing paths at runtime will be very custom, and up to the extension to do, not WXT. But applying a default path is a good start. -
Open sidepanel when clicking action: This more applies to all three above cases. Would be nice to if there was a way to tell WXT to auatomically call
setPanelBehavior({ openPanelOnActionClick: true })for all the cases mentioned above.
If we need to support combining a default panel with other panel's specific to websites, we could use a custom named <name>.sidepanel.html entrypoint alongside the plain sidepanel.html file, which would be the default path.
So with that in mind, here's the changes I would recommend we make:
<meta name="wxt.matchPatterns" value="['*://*.wxt.dev/*']"></meta>
<meta name="wxt.openOnActionClick" value="true"></meta>
- If you don't specify any
wxt.matchPatterns, theside_panel.default_pathwill be added to the manifest and thesidepanelpermission will be added as well. - If you specify
wxt.matchPatterns, theside_panelobject won't be added to the manifest, thetabsandsidepanelpermission will be added, and the background code to watch tabs will be added automatically - If you specify
wxt.openOnActionClick=true, we'll automatically callsetPanelBehavior({ openPanelOnActionClick: true })for you.
If there are multiple sidepanel entrypoints, we should only allow one without any match patterns.
How will we write that
background code to watch tabs
programmatically?
I've got the options from all the sidepanels. I've also conditionally generated manifest.
With addWxtPlugin or via a custom Vite transform. Probably with a vite transform.
Can we use magicast to write that code in background.ts file?
Yes.
But actually there's a third option: implement #1038 and add a virtual background entrypoint that does this. That would probably be the simplest option. I would prefer we do that.
Hi, Is there any sample code I can refer to? I set <meta name="wxt.matchPatterns" value="['*://*.wxt.dev/*']"></meta> <meta name="wxt.openOnActionClick" value="true"></meta> but it didn't work
@larryteal have you been able to find example for WXT ? because using method google suggested and this meta tags does not limit sidepanel for specific sites
@larryteal have you been able to find example for WXT ? because using method google suggested and this meta tags does not limit sidepanel for specific sites
I gave up when I couldn't find any example to refer to.
I ended up using MatchPattern to limit it — not the most elegant solution, but it works well enough for me.
Here's a code snippet for reference:
background/index.ts
const SIDEPANEL_MATCH_PATTERN = new MatchPattern("*://*.wxt.dev/*");
export default defineBackground(() => {
browser.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
browser.tabs.onUpdated.addListener(async (tabId, info, tab) => {
if (!tab.url) return;
if (SIDEPANEL_MATCH_PATTERN.includes(tab.url)) {
// Enables the side panel on wxt.dev
await browser.sidePanel.setOptions({
tabId,
path: "home.html",
enabled: true,
});
} else {
// Disables the side panel on all other sites
await browser.sidePanel.setOptions({
tabId,
enabled: false,
});
}
});
}
wxt.config.ts
import { defineConfig } from "wxt";
// See https://wxt.dev/api/config.html
export default defineConfig({
extensionApi: "chrome",
modules: ["@wxt-dev/module-react"],
manifest: () => ({
action: {},
icons: {
16: "icon/icon-16.png",
48: "icon/icon-48.png",
128: "icon/icon-128.png",
},
permissions: ["tabs", "sidePanel", "storage"],
host_permissions: ["*://*.wxt.dev/*"],
web_accessible_resources: [
{
resources: ["fgod5ga9hbf.js"],
matches: JSON.parse(import.meta.env.VITE_URL_MATCHES),
},
],
}),
hooks: {
'build:manifestGenerated': (wxt, manifest) => {
// @ts-ignore
delete manifest.side_panel;
},
},
vite: () => ({
build: {
chunkSizeWarningLimit: 2000
}
})
});