@extension.dev/mcp 3.17.0-canary.1779905934.1151df3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +101 -0
  3. package/bin/extension-mcp.js +3 -0
  4. package/claude/ARCHITECTURE.md +161 -0
  5. package/claude/CLAUDE.md +264 -0
  6. package/claude/README.md +72 -0
  7. package/claude/commands/extension-add.md +63 -0
  8. package/claude/commands/extension-debug.md +43 -0
  9. package/claude/commands/extension-publish.md +62 -0
  10. package/claude/commands/extension.md +76 -0
  11. package/claude/examples/add-sidebar.md +56 -0
  12. package/claude/examples/create-extension.md +49 -0
  13. package/claude/rules/cross-browser.md +84 -0
  14. package/claude/rules/extension-dev.md +83 -0
  15. package/claude/rules/mcp-tools.md +889 -0
  16. package/dist/module.cjs +2979 -0
  17. package/dist/module.d.ts +1 -0
  18. package/dist/rslib.config.d.ts +2 -0
  19. package/dist/src/index.d.ts +1 -0
  20. package/dist/src/lib/act.d.ts +21 -0
  21. package/dist/src/lib/cdp.d.ts +48 -0
  22. package/dist/src/lib/exec.d.ts +13 -0
  23. package/dist/src/lib/process-manager.d.ts +5 -0
  24. package/dist/src/lib/templates-cache.d.ts +11 -0
  25. package/dist/src/lib/types.d.ts +68 -0
  26. package/dist/src/tools/add-feature.d.ts +29 -0
  27. package/dist/src/tools/build.d.ts +36 -0
  28. package/dist/src/tools/create.d.ts +29 -0
  29. package/dist/src/tools/detect-browsers.d.ts +20 -0
  30. package/dist/src/tools/dev.d.ts +34 -0
  31. package/dist/src/tools/dom-inspect.d.ts +56 -0
  32. package/dist/src/tools/eval.d.ts +44 -0
  33. package/dist/src/tools/get-template-source.d.ts +25 -0
  34. package/dist/src/tools/inspect.d.ts +29 -0
  35. package/dist/src/tools/install-browser.d.ts +18 -0
  36. package/dist/src/tools/list-browsers.d.ts +9 -0
  37. package/dist/src/tools/list-templates.d.ts +41 -0
  38. package/dist/src/tools/logs.d.ts +80 -0
  39. package/dist/src/tools/manifest-validate.d.ts +26 -0
  40. package/dist/src/tools/open.d.ts +31 -0
  41. package/dist/src/tools/preview.d.ts +23 -0
  42. package/dist/src/tools/publish.d.ts +32 -0
  43. package/dist/src/tools/reload.d.ts +33 -0
  44. package/dist/src/tools/source-inspect.d.ts +57 -0
  45. package/dist/src/tools/start.d.ts +29 -0
  46. package/dist/src/tools/storage.d.ts +51 -0
  47. package/dist/src/tools/wait.d.ts +29 -0
  48. package/dist/vitest.config.d.ts +3 -0
  49. package/package.json +84 -0
@@ -0,0 +1,63 @@
1
+ ---
2
+ description: Add a feature surface (sidebar, popup, content script, etc.) to an existing extension
3
+ argument-hint: "<feature> [framework]"
4
+ ---
5
+
6
+ Add a new feature surface to the current extension project. The user said: $ARGUMENTS
7
+
8
+ ## Parse arguments
9
+
10
+ - First argument: feature type — one of: `sidebar`, `popup`, `content-script`, `background`, `newtab`, `options`, `devtools`
11
+ - Second argument (optional): framework — one of: `react`, `vue`, `svelte`, `preact`, `vanilla`. Default: detect from existing project, fall back to `react`
12
+
13
+ ## Steps
14
+
15
+ 1. **Validate the project**
16
+ - Check that `src/manifest.json` exists
17
+ - Read it to understand what's already configured
18
+ - Detect the framework from existing dependencies in `package.json`
19
+
20
+ 2. **Get the reference pattern**
21
+ If MCP tool `extension_add_feature` is available, use it — it returns the exact manifest additions, files to create, and reference template.
22
+
23
+ If MCP tool `extension_get_template_source` is available, read the reference template source to get real implementation patterns.
24
+
25
+ 3. **Update manifest.json**
26
+ Add the required fields to `src/manifest.json`. Use the extension.dev cross-browser format:
27
+
28
+ | Feature | Chromium | Firefox |
29
+ | -------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
30
+ | Sidebar | `"chromium:side_panel": {"default_path": "sidebar/index.html"}` + `"chromium:permissions": ["sidePanel"]` | `"firefox:sidebar_action": {"default_panel": "sidebar/index.html"}` |
31
+ | Popup | `"chromium:action": {"default_popup": "action/index.html"}` | `"firefox:browser_action": {"default_popup": "action/index.html"}` |
32
+ | Content script | `"content_scripts": [{"matches": ["<all_urls>"], "js": ["content/scripts.ts"]}]` | Same |
33
+ | Background | `"background": {"chromium:service_worker": "background.ts", "firefox:scripts": ["background.ts"]}` | Same (prefixed) |
34
+ | New tab | `"chrome_url_overrides": {"newtab": "newtab/index.html"}` | Same |
35
+ | Options | `"options_ui": {"page": "options/index.html", "open_in_tab": true}` | Same |
36
+ | DevTools | `"devtools_page": "devtools/index.html"` | Same |
37
+
38
+ 4. **Create the files**
39
+ For HTML-based features (sidebar, popup, newtab, options, devtools):
40
+ - `src/<feature>/index.html` — HTML entry with script tag
41
+ - `src/<feature>/scripts.tsx` — Framework mount point (or `.ts` for vanilla)
42
+ - `src/<feature>/styles.css` — Stylesheet
43
+ - `src/<feature>/<Feature>App.tsx` — Main component (non-vanilla only)
44
+
45
+ For content scripts:
46
+ - `src/content/scripts.ts` — Entry point
47
+ - `src/content/styles.css` — Injected styles
48
+
49
+ For background:
50
+ - `src/background.ts` — Service worker / background script
51
+
52
+ 5. **Handle sidebar specifically**
53
+ If adding a sidebar, also create/update `src/background.ts`:
54
+
55
+ ```typescript
56
+ // Chromium: open sidebar on action click
57
+ chrome.sidePanel?.setPanelBehavior?.({ openPanelOnActionClick: true });
58
+
59
+ // Firefox: sidebar_action handles this automatically
60
+ ```
61
+
62
+ 6. **Report what was done**
63
+ List all files created and manifest changes made. Suggest `npm run dev` to test.
@@ -0,0 +1,43 @@
1
+ ---
2
+ description: Debug a running extension — inspect DOM, console, content script injection
3
+ argument-hint: "[url] [selectors...]"
4
+ ---
5
+
6
+ Debug the currently running extension dev session. The user said: $ARGUMENTS
7
+
8
+ ## Steps
9
+
10
+ 1. **Check for a running dev session**
11
+ - Look for `dist/extension-js/chrome/ready.json` in the project root
12
+ - If MCP tool `extension_wait` is available, use it with a short timeout (3s) to check
13
+ - If no session: tell the user to start one with `/extension dev` or `npm run dev`
14
+
15
+ 2. **Inspect the live state**
16
+ If MCP tool `extension_source_inspect` is available:
17
+ - Pass `include: ["html", "summary", "meta", "console", "extension_roots"]`
18
+ - If the user provided a URL in `$ARGUMENTS`, pass it as `url`
19
+ - If the user provided CSS selectors (strings starting with `#`, `.`, or `[`), pass them as `probe`
20
+ - Report the results in a structured way
21
+
22
+ If MCP is not available:
23
+ - Suggest the CLI equivalent: `npx extension dev --source <url> --source-format json --source-console`
24
+ - Read `dist/extension-js/chrome/ready.json` to get the CDP port
25
+ - Report the port and suggest Chrome DevTools inspection
26
+
27
+ 3. **Diagnose common issues**
28
+ Based on what you find, check for:
29
+ - **"It didn't load"**: Check extension root count. If 0, content scripts may not be injecting. Check manifest `content_scripts` matches patterns and the target URL.
30
+ - **Console errors**: Report top errors. Common ones:
31
+ - `Uncaught TypeError` — likely a missing import or wrong module format
32
+ - `Content Security Policy` — extension CSP blocking inline scripts
33
+ - `chrome.runtime.lastError` — API permission missing
34
+ - **Wrong page**: Check if the content script `matches` pattern in manifest covers the target URL
35
+ - **Shadow DOM empty**: Extension root exists but shadow content is empty — likely a CSS or framework mounting issue
36
+
37
+ 4. **Suggest fixes** based on the diagnosis
38
+
39
+ ## Examples
40
+
41
+ - `/extension-debug` — inspect the default page target
42
+ - `/extension-debug https://example.com` — inspect a specific URL
43
+ - `/extension-debug https://example.com #my-root .sidebar` — inspect URL and probe selectors
@@ -0,0 +1,62 @@
1
+ ---
2
+ description: Prepare an extension for store submission (Chrome Web Store, Firefox Add-ons)
3
+ argument-hint: "[chrome|firefox|both]"
4
+ ---
5
+
6
+ Prepare the current extension for store submission. The user said: $ARGUMENTS
7
+
8
+ ## Parse arguments
9
+
10
+ Default to `both` (Chrome + Firefox). If the user specifies `chrome` or `firefox`, target only that store.
11
+
12
+ ## Steps
13
+
14
+ 1. **Validate the manifest**
15
+ If MCP tool `extension_manifest_validate` is available, use it with the target browsers.
16
+ Otherwise, read `src/manifest.json` and check:
17
+ - Has `name`, `version`, `description`
18
+ - Has appropriate `manifest_version` for each target
19
+ - Permissions are minimal (no unnecessary permissions)
20
+ - Has icons (at least 16x16, 48x48, 128x128)
21
+
22
+ 2. **Build for each target browser**
23
+
24
+ ```bash
25
+ npx extension build --browser=chrome --zip
26
+ npx extension build --browser=firefox --zip
27
+ ```
28
+
29
+ 3. **Inspect the builds**
30
+ If MCP tool `extension_inspect` is available, use it for each browser build.
31
+ Check:
32
+ - Total size under 10MB (store limit)
33
+ - No source maps in production build
34
+ - Has manifest.json in dist
35
+ - Has icons
36
+
37
+ 4. **Report store readiness**
38
+
39
+ ### Chrome Web Store
40
+ - Zip location: `dist/chrome/<name>.zip`
41
+ - Submit at: https://chrome.google.com/webstore/devconsole
42
+ - Checklist:
43
+ - [ ] manifest_version: 3
44
+ - [ ] Icons: 128x128 PNG
45
+ - [ ] Description under 132 characters (for listing)
46
+ - [ ] Screenshots: 1280x800 or 640x400
47
+ - [ ] Privacy policy URL (if using sensitive permissions)
48
+
49
+ ### Firefox Add-ons (AMO)
50
+ - Zip location: `dist/firefox/<name>.zip`
51
+ - Submit at: https://addons.mozilla.org/developers/
52
+ - Checklist:
53
+ - [ ] manifest_version: 2 (recommended for broadest compat) or 3
54
+ - [ ] No Chrome-only APIs without polyfill
55
+ - [ ] Source code zip if using a bundler: `npx extension build --browser=firefox --zip --zip-source`
56
+ - [ ] AMO requires source code review for minified/bundled code
57
+
58
+ 5. **Flag issues** that would cause store rejection:
59
+ - `<all_urls>` host permission without justification
60
+ - `activeTab` + `scripting` without clear use case
61
+ - Remote code loading (eval, Function constructor, remote scripts)
62
+ - Excessive permissions for the extension's functionality
@@ -0,0 +1,76 @@
1
+ ---
2
+ description: Create, develop, or build a browser extension with extension.dev
3
+ argument-hint: "<action> [options]"
4
+ ---
5
+
6
+ You are helping build a browser extension with the extension.dev platform. The user said: $ARGUMENTS
7
+
8
+ ## Actions
9
+
10
+ Parse the user's intent from `$ARGUMENTS` and execute the matching action:
11
+
12
+ ### "create <name>" or "new <name>" — Scaffold a new extension
13
+
14
+ 1. If MCP tool `extension_list_templates` is available, use it to find the best template matching the user's description (check for surface type, framework, and keywords)
15
+ 2. If not, check the template catalog: `curl -sL https://github.com/extension-js/examples/releases/download/nightly/templates-meta.json | jq '.templates[] | {slug, description, uiFramework, surfaces}'`
16
+ 3. Run `npx extension@latest create <name> --template=<best-match>`
17
+ 4. Report what was created and suggest `npm run dev`
18
+
19
+ ### "dev" or "run" — Start development
20
+
21
+ 1. Run `npm run dev` (or `npx extension dev`) in the project root
22
+ 2. Tell the user the browser will open with their extension loaded
23
+ 3. Mention HMR is active — changes will hot-reload
24
+
25
+ ### "build" — Build for production
26
+
27
+ 1. Run `npm run build` (or `npx extension build`)
28
+ 2. After success, report the output in `dist/chrome/`
29
+ 3. If the user mentions "firefox" or "both", also build with `--browser=firefox`
30
+ 4. If they mention "zip" or "store", add `--zip`
31
+
32
+ ### "add <feature>" — Add a feature surface
33
+
34
+ 1. If MCP tool `extension_add_feature` is available, use it to get the manifest additions and file list
35
+ 2. Otherwise, determine what's needed from the feature type:
36
+ - **sidebar**: `chromium:side_panel` + `firefox:sidebar_action` + `sidePanel` permission + background handler
37
+ - **popup**: `chromium:action` + `firefox:browser_action`
38
+ - **content-script**: `content_scripts` array in manifest
39
+ - **newtab**: `chrome_url_overrides.newtab`
40
+ - **background**: `background.service_worker` (Chromium) + `background.scripts` (Firefox)
41
+ 3. Create the files and update `src/manifest.json`
42
+
43
+ ### "debug" or "inspect" — Debug a running extension
44
+
45
+ 1. If MCP tool `extension_source_inspect` is available, use it with `include: ["html", "console", "extension_roots"]`
46
+ 2. If there's a URL mentioned, pass it as the target
47
+ 3. If there are CSS selectors mentioned, pass them as `probe`
48
+ 4. Report: injected HTML, console errors, extension root state
49
+
50
+ ### "validate" — Check manifest for issues
51
+
52
+ 1. If MCP tool `extension_manifest_validate` is available, use it
53
+ 2. Otherwise, read `src/manifest.json` and check:
54
+ - Required fields: `name`, `manifest_version`
55
+ - Cross-browser: `chromium:` and `firefox:` prefixed fields match
56
+ - Permissions: `sidePanel` present if `side_panel` is declared
57
+ - Background: `service_worker` (Chromium) and `scripts` (Firefox) both present
58
+
59
+ ### "template <query>" — Search for a template
60
+
61
+ 1. If MCP tool `extension_list_templates` is available, use it with the query
62
+ 2. Otherwise, search the catalog JSON
63
+ 3. Show matching templates with slug, description, framework, and surfaces
64
+
65
+ ### No action / general question
66
+
67
+ If the user's input doesn't match an action, treat it as a description of what they want to build. Recommend the best template and offer to create it.
68
+
69
+ ## Cross-browser rules
70
+
71
+ Always use the extension.dev cross-browser manifest format:
72
+
73
+ - `chromium:manifest_version: 3`, `firefox:manifest_version: 2`
74
+ - `chromium:action` vs `firefox:browser_action`
75
+ - `chromium:side_panel` vs `firefox:sidebar_action`
76
+ - `background.chromium:service_worker` vs `background.firefox:scripts`
@@ -0,0 +1,56 @@
1
+ # Example: Add a sidebar panel to an existing extension
2
+
3
+ ## Prompt
4
+
5
+ > Add a sidebar panel to my extension that shows a settings page.
6
+
7
+ ## How Claude should approach this
8
+
9
+ 1. **Update manifest.json** — add side panel config for both browsers:
10
+
11
+ ```json
12
+ {
13
+ "chromium:side_panel": {
14
+ "default_path": "sidebar/index.html"
15
+ },
16
+ "firefox:sidebar_action": {
17
+ "default_panel": "sidebar/index.html"
18
+ },
19
+ "chromium:permissions": ["sidePanel"]
20
+ }
21
+ ```
22
+
23
+ 2. **Add action button** to trigger the sidebar:
24
+
25
+ ```json
26
+ {
27
+ "chromium:action": { "default_title": "Open Settings" },
28
+ "firefox:browser_action": { "default_title": "Open Settings" }
29
+ }
30
+ ```
31
+
32
+ 3. **Create background.ts** to handle the action click:
33
+
34
+ ```typescript
35
+ const isFirefoxLike =
36
+ import.meta.env.EXTENSION_PUBLIC_BROWSER === "firefox" ||
37
+ import.meta.env.EXTENSION_PUBLIC_BROWSER === "gecko-based";
38
+
39
+ if (isFirefoxLike) {
40
+ browser.browserAction.onClicked.addListener(() => {
41
+ browser.sidebarAction.open();
42
+ });
43
+ } else {
44
+ chrome.action.onClicked.addListener(() => {
45
+ chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
46
+ });
47
+ }
48
+ ```
49
+
50
+ 4. **Create the sidebar UI** — `src/sidebar/index.html`, `src/sidebar/scripts.tsx`, `src/sidebar/styles.css`
51
+
52
+ 5. **Test both browsers:**
53
+ ```bash
54
+ npm run dev -- --browser=chrome
55
+ npm run dev -- --browser=firefox
56
+ ```
@@ -0,0 +1,49 @@
1
+ # Example: Create a new extension from scratch
2
+
3
+ ## Prompt
4
+
5
+ > Create a Chrome extension that adds a floating button to every page. When clicked, it summarizes the page content and shows the summary in a popup overlay.
6
+
7
+ ## How Claude should approach this
8
+
9
+ 1. **Scaffold with extension.js:**
10
+
11
+ ```bash
12
+ npx extension@latest create page-summarizer --template=react
13
+ cd page-summarizer
14
+ ```
15
+
16
+ 2. **Define the manifest** — content script for the floating button, background script for orchestration:
17
+
18
+ ```json
19
+ {
20
+ "chromium:manifest_version": 3,
21
+ "firefox:manifest_version": 2,
22
+ "name": "Page Summarizer",
23
+ "content_scripts": [
24
+ {
25
+ "matches": ["<all_urls>"],
26
+ "js": ["content/scripts.tsx"],
27
+ "css": ["content/styles.css"]
28
+ }
29
+ ],
30
+ "background": {
31
+ "chromium:service_worker": "background.ts",
32
+ "firefox:scripts": ["background.ts"]
33
+ },
34
+ "permissions": ["activeTab"]
35
+ }
36
+ ```
37
+
38
+ 3. **Build the content script** — inject a floating button, handle click to extract `document.body.innerText`, send to background for processing.
39
+
40
+ 4. **Test it:**
41
+ ```bash
42
+ npm run dev
43
+ ```
44
+
45
+ ## Key decisions Claude should make
46
+
47
+ - Content script for page injection (not popup, since it needs page context)
48
+ - Background script for any API calls (content scripts have CORS restrictions)
49
+ - Use `chrome.runtime.sendMessage` for content <-> background communication
@@ -0,0 +1,84 @@
1
+ # Cross-Browser Rules for Claude
2
+
3
+ ## Manifest field mapping
4
+
5
+ | Feature | Chromium | Firefox |
6
+ | --------------------- | ------------------------------------- | ----------------------------- |
7
+ | Manifest version | `chromium:manifest_version: 3` | `firefox:manifest_version: 2` |
8
+ | Toolbar button | `chromium:action` | `firefox:browser_action` |
9
+ | Side panel | `chromium:side_panel` | `firefox:sidebar_action` |
10
+ | Background | `chromium:service_worker` (string) | `firefox:scripts` (array) |
11
+ | Side panel permission | `chromium:permissions: ["sidePanel"]` | Not needed |
12
+
13
+ ## Side panel / Sidebar
14
+
15
+ Chromium:
16
+
17
+ ```json
18
+ {
19
+ "chromium:side_panel": {
20
+ "default_path": "sidebar/index.html"
21
+ },
22
+ "chromium:permissions": ["sidePanel"]
23
+ }
24
+ ```
25
+
26
+ Firefox:
27
+
28
+ ```json
29
+ {
30
+ "firefox:sidebar_action": {
31
+ "default_panel": "sidebar/index.html"
32
+ }
33
+ }
34
+ ```
35
+
36
+ Background script to open:
37
+
38
+ ```typescript
39
+ if (isFirefoxLike) {
40
+ browser.browserAction.onClicked.addListener(() => {
41
+ browser.sidebarAction.open();
42
+ });
43
+ } else {
44
+ chrome.action.onClicked.addListener(() => {
45
+ chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
46
+ });
47
+ }
48
+ ```
49
+
50
+ ## Content scripts with world: "MAIN"
51
+
52
+ `world: "MAIN"` only works on Chromium. Must be prefixed:
53
+
54
+ ```json
55
+ {
56
+ "content_scripts": [
57
+ {
58
+ "matches": ["<all_urls>"],
59
+ "js": ["content/scripts.ts"],
60
+ "chromium:world": "MAIN"
61
+ }
62
+ ]
63
+ }
64
+ ```
65
+
66
+ Firefox will ignore the `chromium:world` field and run in the default isolated world.
67
+
68
+ ## API differences
69
+
70
+ - Chromium: use `chrome.*` namespace for Chrome-specific APIs (sidePanel, etc.)
71
+ - Firefox: use `browser.*` namespace (auto-polyfilled by the framework)
72
+ - For cross-browser code: use `browser.*` when possible — the polyfill maps it to `chrome.*` on Chromium
73
+
74
+ ## Testing across browsers
75
+
76
+ ```bash
77
+ # Dev mode
78
+ npm run dev -- --browser=chrome
79
+ npm run dev -- --browser=firefox
80
+ npm run dev -- --browser=edge
81
+
82
+ # Build for multiple browsers
83
+ npm run build -- --browser=chrome,firefox
84
+ ```
@@ -0,0 +1,83 @@
1
+ # extension.dev Development Rules for Claude
2
+
3
+ ## File creation order
4
+
5
+ When Claude creates a new extension, follow this order:
6
+
7
+ 1. `manifest.json` — always first, defines the extension surface
8
+ 2. Background script (if needed) — handles browser events
9
+ 3. UI entry points — HTML files referenced by manifest
10
+ 4. UI scripts — React/Vue/Svelte components mounted into HTML
11
+ 5. Styles — CSS/Tailwind files imported by scripts
12
+ 6. `package.json` — dependencies based on what was used above
13
+ 7. Config files — tsconfig.json, postcss.config.js, extension.config.js only if needed
14
+
15
+ ## manifest.json rules
16
+
17
+ - Always use the `$schema` field for validation: `"$schema": "https://json.schemastore.org/chrome-manifest.json"`
18
+ - Use `chromium:manifest_version: 3` and `firefox:manifest_version: 2` for cross-browser
19
+ - Icon paths are relative to `src/` (where manifest.json lives)
20
+ - Entry point paths (HTML, scripts) are relative to `src/`
21
+ - Always include icons at sizes: 16, 32, 48, 64, 128
22
+
23
+ ## Script entry points
24
+
25
+ - Background: referenced in `manifest.json` under `background`
26
+ - Content scripts: referenced in `manifest.json` under `content_scripts`
27
+ - UI pages (popup, sidebar, options, newtab): referenced as HTML files in manifest, include a `<script src="./scripts.tsx">` tag
28
+
29
+ ## HTML page pattern
30
+
31
+ ```html
32
+ <!DOCTYPE html>
33
+ <html lang="en">
34
+ <head>
35
+ <meta charset="utf-8" />
36
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
37
+ <title>Page Title</title>
38
+ </head>
39
+ <body>
40
+ <noscript>You need to enable JavaScript to run this extension.</noscript>
41
+ <div id="root"></div>
42
+ </body>
43
+ <script src="./scripts.tsx"></script>
44
+ </html>
45
+ ```
46
+
47
+ ## React mounting pattern
48
+
49
+ ```tsx
50
+ import React from "react";
51
+ import ReactDOM from "react-dom/client";
52
+ import App from "./App";
53
+ import "./styles.css";
54
+
55
+ const root = ReactDOM.createRoot(document.getElementById("root")!);
56
+ root.render(
57
+ <React.StrictMode>
58
+ <App />
59
+ </React.StrictMode>,
60
+ );
61
+ ```
62
+
63
+ ## Browser detection
64
+
65
+ ```typescript
66
+ const isFirefoxLike =
67
+ import.meta.env.EXTENSION_PUBLIC_BROWSER === "firefox" ||
68
+ import.meta.env.EXTENSION_PUBLIC_BROWSER === "gecko-based";
69
+ ```
70
+
71
+ ## Storage API
72
+
73
+ Use `chrome.storage.local` for persistent data. It works cross-browser when the polyfill is active (default).
74
+
75
+ ## Permissions
76
+
77
+ Only request permissions the extension actually needs. Common ones:
78
+
79
+ - `sidePanel` — Chromium only, for side panel UI
80
+ - `storage` — for chrome.storage API
81
+ - `activeTab` — for accessing the current tab
82
+ - `tabs` — for tab management
83
+ - `scripting` — for programmatic script injection