feat(popup): Add Firefox default_area and theme_icons support (#2097)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
This commit is contained in:
@@ -470,6 +470,19 @@ When you define a Newtab entrypoint, WXT will automatically update the manifest
|
||||
/>
|
||||
<meta name="manifest.type" content="page_action|browser_action" />
|
||||
<meta name="manifest.browser_style" content="true|false" />
|
||||
<!-- Firefox only: where to place the action button -->
|
||||
<meta
|
||||
name="manifest.default_area"
|
||||
content="navbar|menupanel|tabstrip|personaltoolbar"
|
||||
/>
|
||||
<!-- Firefox only: icons for light/dark themes -->
|
||||
<meta
|
||||
name="manifest.theme_icons"
|
||||
content="[
|
||||
{ light: '/icon-light-16.png', dark: '/icon-dark-16.png', size: 16 },
|
||||
{ light: '/icon-light-32.png', dark: '/icon-dark-32.png', size: 32 }
|
||||
]"
|
||||
/>
|
||||
|
||||
<!-- Set include/exclude if the page should be removed from some builds -->
|
||||
<meta name="manifest.include" content="['chrome', ...]" />
|
||||
|
||||
@@ -106,6 +106,88 @@ describe('Manifest Utils', () => {
|
||||
expect(actual[expectedType]).toEqual(expected);
|
||||
},
|
||||
);
|
||||
|
||||
it('should include default_area for Firefox in mv3', async () => {
|
||||
const popup = fakePopupEntrypoint({
|
||||
options: {
|
||||
// @ts-expect-error: Force this to be undefined
|
||||
mv2Key: null,
|
||||
defaultArea: 'navbar',
|
||||
},
|
||||
outputDir: outDir,
|
||||
skipped: false,
|
||||
});
|
||||
const buildOutput = fakeBuildOutput();
|
||||
setFakeWxt({
|
||||
config: {
|
||||
manifestVersion: 3,
|
||||
outDir,
|
||||
},
|
||||
});
|
||||
|
||||
const { manifest: actual } = await generateManifest(
|
||||
[popup],
|
||||
buildOutput,
|
||||
);
|
||||
|
||||
expect((actual.action as any).default_area).toBe('navbar');
|
||||
});
|
||||
|
||||
it('should include theme_icons for Firefox in mv3', async () => {
|
||||
const themeIcons = [
|
||||
{ light: '/icon-light-16.png', dark: '/icon-dark-16.png', size: 16 },
|
||||
{ light: '/icon-light-32.png', dark: '/icon-dark-32.png', size: 32 },
|
||||
];
|
||||
const popup = fakePopupEntrypoint({
|
||||
options: {
|
||||
// @ts-expect-error: Force this to be undefined
|
||||
mv2Key: null,
|
||||
themeIcons,
|
||||
},
|
||||
outputDir: outDir,
|
||||
skipped: false,
|
||||
});
|
||||
const buildOutput = fakeBuildOutput();
|
||||
setFakeWxt({
|
||||
config: {
|
||||
manifestVersion: 3,
|
||||
outDir,
|
||||
},
|
||||
});
|
||||
|
||||
const { manifest: actual } = await generateManifest(
|
||||
[popup],
|
||||
buildOutput,
|
||||
);
|
||||
|
||||
expect((actual.action as any).theme_icons).toEqual(themeIcons);
|
||||
});
|
||||
|
||||
it('should include default_area for Firefox in mv2', async () => {
|
||||
const popup = fakePopupEntrypoint({
|
||||
options: {
|
||||
// @ts-expect-error: Force this to be undefined
|
||||
mv2Key: null,
|
||||
defaultArea: 'menupanel',
|
||||
},
|
||||
outputDir: outDir,
|
||||
skipped: false,
|
||||
});
|
||||
const buildOutput = fakeBuildOutput();
|
||||
setFakeWxt({
|
||||
config: {
|
||||
manifestVersion: 2,
|
||||
outDir,
|
||||
},
|
||||
});
|
||||
|
||||
const { manifest: actual } = await generateManifest(
|
||||
[popup],
|
||||
buildOutput,
|
||||
);
|
||||
|
||||
expect((actual.browser_action as any).default_area).toBe('menupanel');
|
||||
});
|
||||
});
|
||||
|
||||
describe('action without popup', () => {
|
||||
|
||||
@@ -276,16 +276,20 @@ async function getPopupEntrypoint(
|
||||
defaultIcon: options.defaultIcon,
|
||||
defaultTitle: options.title,
|
||||
mv2Key: options.type,
|
||||
defaultArea: options.defaultArea,
|
||||
},
|
||||
wxt.config.browser,
|
||||
);
|
||||
if (stictOptions.mv2Key && stictOptions.mv2Key !== 'page_action')
|
||||
stictOptions.mv2Key = 'browser_action';
|
||||
|
||||
// themeIcons is an array of objects, not a per-browser option
|
||||
const themeIcons = options.themeIcons;
|
||||
|
||||
return {
|
||||
type: 'popup',
|
||||
name: 'popup',
|
||||
options: stictOptions,
|
||||
options: { ...stictOptions, themeIcons },
|
||||
inputPath: info.inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
};
|
||||
|
||||
@@ -281,6 +281,12 @@ function addEntrypoints(
|
||||
if (popup.options.browserStyle)
|
||||
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
|
||||
options.browser_style = popup.options.browserStyle;
|
||||
if (popup.options.defaultArea)
|
||||
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
|
||||
options.default_area = popup.options.defaultArea;
|
||||
if (popup.options.themeIcons)
|
||||
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
|
||||
options.theme_icons = popup.options.themeIcons;
|
||||
if (manifest.manifest_version === 3) {
|
||||
manifest.action = {
|
||||
...manifest.action,
|
||||
|
||||
@@ -146,6 +146,10 @@ export const fakePopupEntrypoint = fakeObjectCreator<PopupEntrypoint>(() => ({
|
||||
'page_action',
|
||||
undefined,
|
||||
]),
|
||||
// Firefox-specific options - kept undefined by default to avoid breaking existing tests
|
||||
browserStyle: undefined,
|
||||
defaultArea: undefined,
|
||||
themeIcons: undefined,
|
||||
},
|
||||
skipped: faker.datatype.boolean(),
|
||||
}));
|
||||
|
||||
@@ -648,6 +648,19 @@ export interface IsolatedWorldContentScriptEntrypointOptions extends BaseContent
|
||||
world?: 'ISOLATED';
|
||||
}
|
||||
|
||||
/**
|
||||
* Firefox theme icon definition for light/dark mode support.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action#theme_icons
|
||||
*/
|
||||
export interface ThemeIcon {
|
||||
/** Path to the icon shown when the browser uses a light theme. */
|
||||
light: string;
|
||||
/** Path to the icon shown when the browser uses a dark theme. */
|
||||
dark: string;
|
||||
/** Icon size in pixels. */
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface PopupEntrypointOptions extends BaseEntrypointOptions {
|
||||
/**
|
||||
* Defaults to "browser_action" to be equivalent to MV3's "action" key
|
||||
@@ -656,6 +669,18 @@ export interface PopupEntrypointOptions extends BaseEntrypointOptions {
|
||||
defaultIcon?: Record<string, string>;
|
||||
defaultTitle?: PerBrowserOption<string>;
|
||||
browserStyle?: PerBrowserOption<boolean>;
|
||||
/**
|
||||
* Firefox only. Defines the part of the browser in which the button is initially placed.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action#default_area
|
||||
*/
|
||||
defaultArea?: PerBrowserOption<
|
||||
'navbar' | 'menupanel' | 'tabstrip' | 'personaltoolbar'
|
||||
>;
|
||||
/**
|
||||
* Firefox only. Icons for light and dark themes.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action#theme_icons
|
||||
*/
|
||||
themeIcons?: ThemeIcon[];
|
||||
}
|
||||
|
||||
export interface OptionsEntrypointOptions extends BaseEntrypointOptions {
|
||||
|
||||
Reference in New Issue
Block a user