@extension.dev/mcp 10.3.1 → 10.4.0
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +21 -0
- package/dist/module.js +98 -89
- package/dist/src/lib/template-artifact-source.d.ts +3 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "extension-mcp",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "MCP tools for browser extension development: scaffold from 50+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox.",
|
|
13
|
-
"version": "10.
|
|
13
|
+
"version": "10.4.0",
|
|
14
14
|
"category": "development",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "Cezar Augusto"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extension-mcp",
|
|
3
3
|
"description": "MCP tools for browser extension development: scaffold from 50+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox. Ships /extension, /extension-add, /extension-debug, and /extension-publish commands.",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.4.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 10.4.0
|
|
4
|
+
|
|
5
|
+
Every place a tool named a template, it pointed the caller at GitHub and
|
|
6
|
+
never at the catalog that serves the same template with screenshots,
|
|
7
|
+
metadata and a deploy button. The MCP was routing around its own front
|
|
8
|
+
door. Template emissions now carry the catalog detail URL alongside the
|
|
9
|
+
existing fields, and the offline fallback corpus moved to the commit the
|
|
10
|
+
catalog actually serves.
|
|
11
|
+
|
|
12
|
+
- `extension_templates` list results gain `catalogUrl`,
|
|
13
|
+
`https://templates.extension.dev/<slug>?utm_source=mcp&utm_medium=tool`,
|
|
14
|
+
beside `repositoryUrl` and `downloads`, which are unchanged. The source
|
|
15
|
+
view and `extension_add_feature`'s reference template carry the same
|
|
16
|
+
field.
|
|
17
|
+
- `extension_create` results gain `templateCatalogUrl` for the template
|
|
18
|
+
the project was scaffolded from, with `utm_source=mcp-create` so
|
|
19
|
+
create-path visits stay distinguishable from browse-path visits.
|
|
20
|
+
- The pinned fallback template corpus moved to `52c0d871c433`, the commit
|
|
21
|
+
the live `latest` channel pointer resolves to, so the offline corpus and
|
|
22
|
+
the catalog agree.
|
|
23
|
+
|
|
3
24
|
## 10.3.1
|
|
4
25
|
|
|
5
26
|
The 10.3.0 tarball shipped two comments a stranger could read: an HTML
|
package/dist/module.js
CHANGED
|
@@ -582,7 +582,7 @@ __webpack_require__.d(wait_namespaceObject, {
|
|
|
582
582
|
handler: ()=>wait_handler,
|
|
583
583
|
schema: ()=>wait_schema
|
|
584
584
|
});
|
|
585
|
-
var package_namespaceObject = JSON.parse('{"rE":"10.
|
|
585
|
+
var package_namespaceObject = JSON.parse('{"rE":"10.4.0","El":{"OP":"4.0.20"}}');
|
|
586
586
|
const ENVELOPE_SCHEMA = 1;
|
|
587
587
|
const collectWarnings = (warnings)=>{
|
|
588
588
|
if (!warnings) return [];
|
|
@@ -1328,6 +1328,97 @@ function parseBuildIndex(json) {
|
|
|
1328
1328
|
}
|
|
1329
1329
|
return out;
|
|
1330
1330
|
}
|
|
1331
|
+
const DEFAULT_MEDIA_ORIGIN = "https://media.extension.land";
|
|
1332
|
+
const DEFAULT_CHANNEL = "latest";
|
|
1333
|
+
const PINNED_COMMIT = "52c0d871c433d9c54878767175ce8ffc9a951756";
|
|
1334
|
+
const CHANNEL_CACHE_TTL_MS = 300000;
|
|
1335
|
+
function mediaOrigin() {
|
|
1336
|
+
return (process.env.EXTENSION_MEDIA_ORIGIN || "").trim() || DEFAULT_MEDIA_ORIGIN;
|
|
1337
|
+
}
|
|
1338
|
+
function channelName() {
|
|
1339
|
+
return (process.env.EXTENSION_TEMPLATES_CHANNEL || "").trim() || DEFAULT_CHANNEL;
|
|
1340
|
+
}
|
|
1341
|
+
function pinnedCommitOverride() {
|
|
1342
|
+
return (process.env.EXTENSION_TEMPLATES_COMMIT || "").trim();
|
|
1343
|
+
}
|
|
1344
|
+
function rawBaseForCommit(commit) {
|
|
1345
|
+
return `https://raw.githubusercontent.com/extension-js/examples/${commit}`;
|
|
1346
|
+
}
|
|
1347
|
+
let releaseCache = null;
|
|
1348
|
+
let releaseCacheExpiresAt = 0;
|
|
1349
|
+
let releaseRequest = null;
|
|
1350
|
+
function releaseForCommit(origin, commit) {
|
|
1351
|
+
return {
|
|
1352
|
+
commit,
|
|
1353
|
+
metaUrl: `${origin}/templates/${commit}/templates-meta.json`,
|
|
1354
|
+
filesBaseUrl: `${origin}/templates/${commit}/files`
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
async function resolveRelease() {
|
|
1358
|
+
const now = Date.now();
|
|
1359
|
+
if (releaseCache && now < releaseCacheExpiresAt) return releaseCache;
|
|
1360
|
+
if (releaseRequest) return releaseRequest;
|
|
1361
|
+
const origin = mediaOrigin();
|
|
1362
|
+
const override = pinnedCommitOverride();
|
|
1363
|
+
if (override) {
|
|
1364
|
+
releaseCache = releaseForCommit(origin, override);
|
|
1365
|
+
releaseCacheExpiresAt = now + CHANNEL_CACHE_TTL_MS;
|
|
1366
|
+
return releaseCache;
|
|
1367
|
+
}
|
|
1368
|
+
releaseRequest = (async ()=>{
|
|
1369
|
+
try {
|
|
1370
|
+
const pointerUrl = `${origin}/templates/${channelName()}.json`;
|
|
1371
|
+
const response = await fetch(pointerUrl, {
|
|
1372
|
+
headers: {
|
|
1373
|
+
Accept: "application/json"
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
if (!response.ok) return null;
|
|
1377
|
+
const pointer = await response.json();
|
|
1378
|
+
const commit = String(pointer?.commit || "").trim();
|
|
1379
|
+
if (!commit) return null;
|
|
1380
|
+
releaseCache = releaseForCommit(origin, commit);
|
|
1381
|
+
releaseCacheExpiresAt = Date.now() + CHANNEL_CACHE_TTL_MS;
|
|
1382
|
+
return releaseCache;
|
|
1383
|
+
} catch {
|
|
1384
|
+
return null;
|
|
1385
|
+
}
|
|
1386
|
+
})();
|
|
1387
|
+
try {
|
|
1388
|
+
return await releaseRequest;
|
|
1389
|
+
} finally{
|
|
1390
|
+
releaseRequest = null;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function templateCatalogUrl(slug, utmSource = "mcp") {
|
|
1394
|
+
const origin = mcpOrigins().templates.replace(/\/+$/, "");
|
|
1395
|
+
return `${origin}/${encodeURIComponent(slug)}?utm_source=${utmSource}&utm_medium=tool`;
|
|
1396
|
+
}
|
|
1397
|
+
async function templateMetaUrls() {
|
|
1398
|
+
const urls = [];
|
|
1399
|
+
const release = await resolveRelease();
|
|
1400
|
+
if (release) urls.push(release.metaUrl);
|
|
1401
|
+
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/templates-meta.json`);
|
|
1402
|
+
return urls;
|
|
1403
|
+
}
|
|
1404
|
+
function stripTemplatePathPrefix(slug, relativePath) {
|
|
1405
|
+
for (const dir of [
|
|
1406
|
+
"public",
|
|
1407
|
+
"examples"
|
|
1408
|
+
]){
|
|
1409
|
+
const prefix = `${dir}/${slug}/`;
|
|
1410
|
+
if (relativePath.startsWith(prefix)) return relativePath.slice(prefix.length);
|
|
1411
|
+
}
|
|
1412
|
+
return relativePath;
|
|
1413
|
+
}
|
|
1414
|
+
async function templateFileUrls(slug, relativePath) {
|
|
1415
|
+
const relative = stripTemplatePathPrefix(slug, relativePath);
|
|
1416
|
+
const urls = [];
|
|
1417
|
+
const release = await resolveRelease();
|
|
1418
|
+
if (release) urls.push(`${release.filesBaseUrl}/${slug}/${relative}`);
|
|
1419
|
+
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/examples/${slug}/${relative}`);
|
|
1420
|
+
return urls;
|
|
1421
|
+
}
|
|
1331
1422
|
const COMMAND = "extension_create";
|
|
1332
1423
|
function scaffoldEnginePin(projectPath) {
|
|
1333
1424
|
try {
|
|
@@ -1495,6 +1586,7 @@ async function create_handler(args) {
|
|
|
1495
1586
|
const deployUrl = `${wwwOrigin}${(0, paths_0.wwwNewPath)({
|
|
1496
1587
|
template: result.template
|
|
1497
1588
|
})}`;
|
|
1589
|
+
const catalogUrl = templateCatalogUrl(result.template, "mcp-create");
|
|
1498
1590
|
return envelope_envelope({
|
|
1499
1591
|
ok: true,
|
|
1500
1592
|
command: COMMAND,
|
|
@@ -1504,6 +1596,7 @@ async function create_handler(args) {
|
|
|
1504
1596
|
projectPath: result.projectPath,
|
|
1505
1597
|
projectName: result.projectName,
|
|
1506
1598
|
template: result.template,
|
|
1599
|
+
templateCatalogUrl: catalogUrl,
|
|
1507
1600
|
depsInstalled: result.depsInstalled,
|
|
1508
1601
|
packageManager: result.depsInstalled ? packageManager : null,
|
|
1509
1602
|
deployUrl,
|
|
@@ -1535,93 +1628,6 @@ async function create_handler(args) {
|
|
|
1535
1628
|
]
|
|
1536
1629
|
});
|
|
1537
1630
|
}
|
|
1538
|
-
const DEFAULT_MEDIA_ORIGIN = "https://media.extension.land";
|
|
1539
|
-
const DEFAULT_CHANNEL = "latest";
|
|
1540
|
-
const PINNED_COMMIT = "520a8ede767fe1937c7184f4857408caf9c5934f";
|
|
1541
|
-
const CHANNEL_CACHE_TTL_MS = 300000;
|
|
1542
|
-
function mediaOrigin() {
|
|
1543
|
-
return (process.env.EXTENSION_MEDIA_ORIGIN || "").trim() || DEFAULT_MEDIA_ORIGIN;
|
|
1544
|
-
}
|
|
1545
|
-
function channelName() {
|
|
1546
|
-
return (process.env.EXTENSION_TEMPLATES_CHANNEL || "").trim() || DEFAULT_CHANNEL;
|
|
1547
|
-
}
|
|
1548
|
-
function pinnedCommitOverride() {
|
|
1549
|
-
return (process.env.EXTENSION_TEMPLATES_COMMIT || "").trim();
|
|
1550
|
-
}
|
|
1551
|
-
function rawBaseForCommit(commit) {
|
|
1552
|
-
return `https://raw.githubusercontent.com/extension-js/examples/${commit}`;
|
|
1553
|
-
}
|
|
1554
|
-
let releaseCache = null;
|
|
1555
|
-
let releaseCacheExpiresAt = 0;
|
|
1556
|
-
let releaseRequest = null;
|
|
1557
|
-
function releaseForCommit(origin, commit) {
|
|
1558
|
-
return {
|
|
1559
|
-
commit,
|
|
1560
|
-
metaUrl: `${origin}/templates/${commit}/templates-meta.json`,
|
|
1561
|
-
filesBaseUrl: `${origin}/templates/${commit}/files`
|
|
1562
|
-
};
|
|
1563
|
-
}
|
|
1564
|
-
async function resolveRelease() {
|
|
1565
|
-
const now = Date.now();
|
|
1566
|
-
if (releaseCache && now < releaseCacheExpiresAt) return releaseCache;
|
|
1567
|
-
if (releaseRequest) return releaseRequest;
|
|
1568
|
-
const origin = mediaOrigin();
|
|
1569
|
-
const override = pinnedCommitOverride();
|
|
1570
|
-
if (override) {
|
|
1571
|
-
releaseCache = releaseForCommit(origin, override);
|
|
1572
|
-
releaseCacheExpiresAt = now + CHANNEL_CACHE_TTL_MS;
|
|
1573
|
-
return releaseCache;
|
|
1574
|
-
}
|
|
1575
|
-
releaseRequest = (async ()=>{
|
|
1576
|
-
try {
|
|
1577
|
-
const pointerUrl = `${origin}/templates/${channelName()}.json`;
|
|
1578
|
-
const response = await fetch(pointerUrl, {
|
|
1579
|
-
headers: {
|
|
1580
|
-
Accept: "application/json"
|
|
1581
|
-
}
|
|
1582
|
-
});
|
|
1583
|
-
if (!response.ok) return null;
|
|
1584
|
-
const pointer = await response.json();
|
|
1585
|
-
const commit = String(pointer?.commit || "").trim();
|
|
1586
|
-
if (!commit) return null;
|
|
1587
|
-
releaseCache = releaseForCommit(origin, commit);
|
|
1588
|
-
releaseCacheExpiresAt = Date.now() + CHANNEL_CACHE_TTL_MS;
|
|
1589
|
-
return releaseCache;
|
|
1590
|
-
} catch {
|
|
1591
|
-
return null;
|
|
1592
|
-
}
|
|
1593
|
-
})();
|
|
1594
|
-
try {
|
|
1595
|
-
return await releaseRequest;
|
|
1596
|
-
} finally{
|
|
1597
|
-
releaseRequest = null;
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
async function templateMetaUrls() {
|
|
1601
|
-
const urls = [];
|
|
1602
|
-
const release = await resolveRelease();
|
|
1603
|
-
if (release) urls.push(release.metaUrl);
|
|
1604
|
-
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/templates-meta.json`);
|
|
1605
|
-
return urls;
|
|
1606
|
-
}
|
|
1607
|
-
function stripTemplatePathPrefix(slug, relativePath) {
|
|
1608
|
-
for (const dir of [
|
|
1609
|
-
"public",
|
|
1610
|
-
"examples"
|
|
1611
|
-
]){
|
|
1612
|
-
const prefix = `${dir}/${slug}/`;
|
|
1613
|
-
if (relativePath.startsWith(prefix)) return relativePath.slice(prefix.length);
|
|
1614
|
-
}
|
|
1615
|
-
return relativePath;
|
|
1616
|
-
}
|
|
1617
|
-
async function templateFileUrls(slug, relativePath) {
|
|
1618
|
-
const relative = stripTemplatePathPrefix(slug, relativePath);
|
|
1619
|
-
const urls = [];
|
|
1620
|
-
const release = await resolveRelease();
|
|
1621
|
-
if (release) urls.push(`${release.filesBaseUrl}/${slug}/${relative}`);
|
|
1622
|
-
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/examples/${slug}/${relative}`);
|
|
1623
|
-
return urls;
|
|
1624
|
-
}
|
|
1625
1631
|
var templates_meta_snapshot_namespaceObject = JSON.parse('{"version":"2","sourceRepo":"extension-js/examples","generatorVersion":"2","commit":"336cfc03c39f9fa52f4ecffd351313241b5ca0a4","generatedAt":"2026-07-16T13:47:35.883Z","templates":[{"slug":"action","name":"action","title":"Action (Popup)","version":"1.0.0","manifest_version":3,"description":"Action popup example. Opens a toolbar popup with a simple page.","uiContext":["background"],"surfaces":["background"],"entrypoints":[],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"tags":["action","popup","javascript"],"difficulty":"beginner","timeToFirstSuccessMinutes":2,"firstSteps":["Run npm run dev to launch the extension","Click the toolbar icon to open the action popup","Edit src/action/index.html and watch HMR update the popup"],"useCases":["Toolbar popup as the primary UI surface","Quick actions that don\'t need a full sidebar or page","Starting point for popup-based browser extensions"],"docsUrl":"https://extension.js.org/docs/implementation-guide/manifest-json","files":["extension.config.js","public/screenshot.png","src/action/index.html","src/action/scripts.js","src/action/styles.css","src/background.js","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/action/public/screenshot.png","icon":"examples/action/src/images/icon.png"},{"slug":"action-locales","name":"action-locales","title":"Action Locales","version":"1.0.0","manifest_version":3,"description":"Action popup example demonstrating i18n with _locales and message placeholders.","uiContext":["background"],"surfaces":["background"],"entrypoints":[],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/action/index.html","src/action/scripts.js","src/action/styles.css","src/background.js","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/action-locales/public/screenshot.png","icon":"examples/action-locales/src/images/icon.png"},{"slug":"ai-chatgpt","name":"ai-chatgpt","title":"ChatGPT AI Sidebar","version":"1.0.0","manifest_version":3,"description":"React sidebar with ChatGPT chat. Adds a side panel with a conversational interface powered by the OpenAI SDK.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"react","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"tags":["ai","chatgpt","openai","gpt","chat","sidebar","content-scripts","react","shadcn"],"difficulty":"beginner","timeToFirstSuccessMinutes":3,"firstSteps":["Run npm run dev to launch the extension","Open any web page and click the extension icon to open the sidebar","Enter your ChatGPT API key (from platform.openai.com)","Ask ChatGPT about the active tab — the content script feeds the page text into the conversation"],"useCases":["Chat with the active tab\'s content using ChatGPT","Summarize or question articles, docs, and dashboards in place","Starting point for page-aware ChatGPT extensions"],"docsUrl":"https://platform.openai.com/docs/api-reference/chat","files":["extension.config.js","public/screenshot.png","src/background.ts","src/components/ApiKeyForm.tsx","src/components/ChatInput.tsx","src/components/ChatMessage.tsx","src/components/ui/button.tsx","src/components/ui/card.tsx","src/components/ui/scroll-area.tsx","src/content/ContentApp.ts","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/lib/client.ts","src/lib/page-context.ts","src/lib/utils.ts","src/manifest.json","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/ai-chatgpt/public/screenshot.png","icon":"examples/ai-chatgpt/src/images/icon.png"},{"slug":"ai-claude","name":"ai-claude","title":"Claude AI Sidebar","version":"1.0.0","manifest_version":3,"description":"React sidebar with Claude AI chat. Adds a side panel with a conversational interface powered by the Anthropic SDK.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"react","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"tags":["ai","claude","anthropic","chat","sidebar","content-scripts","react","shadcn"],"difficulty":"beginner","timeToFirstSuccessMinutes":3,"firstSteps":["Run npm run dev to launch the extension","Open any web page and click the extension icon to open the sidebar","Enter your Claude API key (from console.anthropic.com)","Ask Claude about the active tab — the content script feeds the page text into the conversation"],"useCases":["Chat with the active tab\'s content using Claude","Summarize or question articles, docs, and dashboards in place","Starting point for page-aware Claude extensions"],"docsUrl":"https://docs.anthropic.com/en/api/getting-started","files":["extension.config.js","public/screenshot.png","src/background.ts","src/components/ApiKeyForm.tsx","src/components/ChatInput.tsx","src/components/ChatMessage.tsx","src/components/ui/button.tsx","src/components/ui/card.tsx","src/components/ui/scroll-area.tsx","src/content/ContentApp.ts","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/lib/client.ts","src/lib/page-context.ts","src/lib/utils.ts","src/manifest.json","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/ai-claude/public/screenshot.png","icon":"examples/ai-claude/src/images/icon.png"},{"slug":"ai-gemini","name":"ai-gemini","title":"Gemini AI Sidebar","version":"1.0.0","manifest_version":3,"description":"React sidebar with Gemini chat. Adds a side panel with a conversational interface powered by the Google Generative AI SDK.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"react","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"tags":["ai","gemini","google","chat","sidebar","content-scripts","react","shadcn"],"difficulty":"beginner","timeToFirstSuccessMinutes":3,"firstSteps":["Run npm run dev to launch the extension","Open any web page and click the extension icon to open the sidebar","Enter your Gemini API key (from aistudio.google.com)","Ask Gemini about the active tab — the content script feeds the page text into the conversation"],"useCases":["Chat with the active tab\'s content using Gemini","Summarize or question articles, docs, and dashboards in place","Starting point for page-aware Gemini extensions"],"docsUrl":"https://ai.google.dev/gemini-api/docs","files":["extension.config.js","public/screenshot.png","src/background.ts","src/components/ApiKeyForm.tsx","src/components/ChatInput.tsx","src/components/ChatMessage.tsx","src/components/ui/button.tsx","src/components/ui/card.tsx","src/components/ui/scroll-area.tsx","src/content/ContentApp.ts","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/lib/client.ts","src/lib/page-context.ts","src/lib/utils.ts","src/manifest.json","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/ai-gemini/public/screenshot.png","icon":"examples/ai-gemini/src/images/icon.png"},{"slug":"ai-perplexity","name":"ai-perplexity","title":"Perplexity AI Sidebar","version":"1.0.0","manifest_version":3,"description":"React sidebar with Perplexity chat. Adds a side panel with a conversational interface powered by the Perplexity API (OpenAI-compatible).","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"react","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"tags":["ai","perplexity","search","chat","sidebar","content-scripts","react","shadcn"],"difficulty":"beginner","timeToFirstSuccessMinutes":3,"firstSteps":["Run npm run dev to launch the extension","Open any web page and click the extension icon to open the sidebar","Enter your Perplexity API key (from perplexity.ai/settings/api)","Ask Perplexity about the active tab — the content script feeds the page text into the conversation"],"useCases":["Chat with the active tab\'s content using Perplexity","Summarize or question articles, docs, and dashboards in place","Starting point for page-aware Perplexity extensions"],"docsUrl":"https://docs.perplexity.ai/","files":["extension.config.js","public/screenshot.png","src/background.ts","src/components/ApiKeyForm.tsx","src/components/ChatInput.tsx","src/components/ChatMessage.tsx","src/components/ui/button.tsx","src/components/ui/card.tsx","src/components/ui/scroll-area.tsx","src/content/ContentApp.ts","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/lib/client.ts","src/lib/page-context.ts","src/lib/utils.ts","src/manifest.json","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/ai-perplexity/public/screenshot.png","icon":"examples/ai-perplexity/src/images/icon.png"},{"slug":"content","name":"@extension-js/content","title":"Content scripts","version":"1.0.0","manifest_version":3,"description":"Content script example that injects a small badge into web pages to demonstrate DOM injection and styling in an extension.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.css","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content/public/screenshot.png","icon":"examples/content/src/images/icon.png"},{"slug":"content-css-modules","name":"@extension-js/content-css-modules","title":"Content CSS Modules","version":"1.0.0","manifest_version":3,"description":"Content script example styled with CSS Modules. Injects a small badge and shows how to scope styles in content scripts.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.module.css","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-css-modules/public/screenshot.png","icon":"examples/content-css-modules/src/images/icon.png"},{"slug":"content-custom-font","name":"content-custom-font","title":"Content Custom Font","version":"1.0.0","manifest_version":3,"description":"Content script example that loads custom web fonts. Demonstrates font files and web_accessible_resources setup.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"css","configFiles":["postcss.config.js","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.css","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-custom-font/public/screenshot.png","icon":"examples/content-custom-font/src/images/icon.png"},{"slug":"content-env","name":"content-env","title":"Content Env","version":"1.0.0","manifest_version":3,"description":"Content script example that reads environment variables (.env) and renders a small UI.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.ts"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-env/public/screenshot.png","icon":"examples/content-env/src/images/icon.png"},{"slug":"content-less","name":"@extension-js/content-less","title":"Content Less","version":"1.0.0","manifest_version":3,"description":"Content script example styled with Less. Injects a small badge and shows how to organize styles with Less in content scripts.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"less","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.less","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-less/public/screenshot.png","icon":"examples/content-less/src/images/icon.png"},{"slug":"content-less-modules","name":"@extension-js/content-less-modules","title":"Content Less Modules","version":"1.0.0","manifest_version":3,"description":"Content script example using Less with CSS Modules. Injects a small badge and shows how to scope Less styles in content scripts.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"less","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.module.less","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-less-modules/public/screenshot.png","icon":"examples/content-less-modules/src/images/icon.png"},{"slug":"content-main-world","name":"content-main-world","title":"Main-world Content Script","version":"1.0.0","manifest_version":3,"description":"Main world content script with multiple groups. ISOLATED entries precede the MAIN world entry so bridge injection exercises canonical-index vs array-position resolution.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/isolated-one.js","content/isolated-two.js","content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"tags":["content-scripts","main-world","isolated-world","javascript"],"difficulty":"intermediate","timeToFirstSuccessMinutes":3,"firstSteps":["Run npm run dev to launch the extension","Open any page and watch the main-world script run alongside isolated entries","Inspect window.postMessage bridges between isolated and main worlds"],"useCases":["Hook into page-level globals that isolated content scripts cannot reach","Bridge isolated and main-world execution contexts cleanly","Reference for multi-group content_scripts in MV3"],"docsUrl":"https://extension.js.org/docs/implementation-guide/content-scripts","files":["extension.config.js","public/screenshot.png","src/background.js","src/content/isolated-one.js","src/content/isolated-two.js","src/content/scripts.js","src/content/styles.css","src/content/utils/constants.js","src/content/utils/create-badge.js","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-main-world/public/screenshot.png","icon":"examples/content-main-world/src/images/icon.png"},{"slug":"content-multi-one-entry","name":"content-multi-one-entry","title":"Content Multi One Entry","version":"1.0.0","manifest_version":3,"description":"Content script example with four scripts listed in one entry. Each script injects a small element.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/script-top-left.js","content/script-top-right.js","content/script-bottom-left.js","content/script-bottom-right.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/script-bottom-left.js","src/content/script-bottom-right.js","src/content/script-top-left.js","src/content/script-top-right.js","src/content/styles.css","src/content/utils/constants.js","src/content/utils/create-badge.js","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-multi-one-entry/public/screenshot.png","icon":"examples/content-multi-one-entry/src/images/icon.png"},{"slug":"content-multi-three-entries","name":"content-multi-three-entries","title":"Multi-entry Content Scripts","version":"1.0.0","manifest_version":3,"description":"Content script example split across three entries (2 + 1 + 1). Each script injects a small element.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/script-top-left.js","content/script-top-right.js","content/script-bottom-left.js","content/script-bottom-right.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"tags":["content-scripts","multi-entry","javascript"],"difficulty":"intermediate","timeToFirstSuccessMinutes":3,"firstSteps":["Run npm run dev to launch the extension","Open any page — three separate content scripts inject small elements","Edit any of the entries in src/content/ to see HMR update independently"],"useCases":["Split content-script logic across multiple entries with different match rules","Target different page contexts with focused, independent bundles","Reference for the multi-entry content_scripts pattern in MV3"],"docsUrl":"https://extension.js.org/docs/implementation-guide/content-scripts","files":["extension.config.js","public/screenshot.png","src/background.js","src/content/script-bottom-left.js","src/content/script-bottom-right.js","src/content/script-top-left.js","src/content/script-top-right.js","src/content/styles.css","src/content/utils/constants.js","src/content/utils/create-badge.js","src/images/icon.png","src/manifest.json","src/screenshot.png"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-multi-three-entries/public/screenshot.png","icon":"examples/content-multi-three-entries/src/images/icon.png"},{"slug":"content-preact","name":"content-preact","title":"Content Preact","version":"1.0.0","manifest_version":3,"description":"Content script example rendered with Preact. Mounts a small UI inside a Shadow DOM on any page.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["./content/scripts.tsx"],"uiFramework":"preact","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","src/background.ts","src/content/ContentApp.tsx","src/content/scripts.tsx","src/content/styles.css","src/images/chromeWindow.png","src/images/icon.png","src/images/preact.png","src/images/tailwind.png","src/images/tailwind_bg.png","src/images/typescript.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-preact/screenshot.png","icon":"examples/content-preact/src/images/icon.png"},{"slug":"content-react","name":"content-react","title":"Content React","version":"1.0.0","manifest_version":3,"description":"Content script example rendered with React. Mounts a small UI inside a Shadow DOM on any page.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["./content/scripts.tsx"],"uiFramework":"react","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","src/background.ts","src/content/ContentApp.tsx","src/content/scripts.tsx","src/content/styles.css","src/images/chromeWindow.png","src/images/icon.png","src/images/react.png","src/images/tailwind.png","src/images/tailwind_bg.png","src/images/typescript.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-react/screenshot.png","icon":"examples/content-react/src/images/icon.png"},{"slug":"content-sass","name":"@extension-js/content-sass","title":"Content Sass","version":"1.0.0","manifest_version":3,"description":"Content script example styled with Sass. Injects a small badge and shows how to organize styles with Sass in content scripts.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"sass","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.scss","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-sass/public/screenshot.png","icon":"examples/content-sass/src/images/icon.png"},{"slug":"content-sass-modules","name":"@extension-js/content-sass-modules","title":"Content Sass Modules","version":"1.0.0","manifest_version":3,"description":"A browser extension content script example built with Extension.js using Sass CSS Modules. Demonstrates scoped styles for content scripts with Sass preprocessing.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"sass","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{"build":"extension build"},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.module.scss","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-sass-modules/public/screenshot.png","icon":"examples/content-sass-modules/src/images/icon.png"},{"slug":"content-svelte","name":"content-svelte","title":"Content Svelte","version":"1.0.0","manifest_version":3,"description":"Content script example rendered with Svelte. Mounts a small UI inside a Shadow DOM on any page.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["./content/scripts.ts"],"uiFramework":"svelte","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","src/background.ts","src/content/ContentApp.svelte","src/content/scripts.ts","src/content/styles.css","src/content/svelte.d.ts","src/images/chromeWindow.png","src/images/icon.png","src/images/svelte.png","src/images/tailwind.png","src/images/tailwind_bg.png","src/images/typescript.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-svelte/screenshot.png","icon":"examples/content-svelte/src/images/icon.png"},{"slug":"content-typescript","name":"content-typescript","title":"Content TypeScript","version":"1.0.0","manifest_version":3,"description":"Content script example built with TypeScript. Mounts a small UI and uses typed modules.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.ts"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-typescript/public/screenshot.png","icon":"examples/content-typescript/src/images/icon.png"},{"slug":"content-vue","name":"content-vue","title":"Content Vue","version":"1.0.0","manifest_version":3,"description":"Content script example rendered with Vue. Mounts a small UI inside a Shadow DOM on any page.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["./content/scripts.ts"],"uiFramework":"vue","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","src/background.ts","src/content/ContentApp.vue","src/content/scripts.ts","src/content/shims-vue.d.ts","src/content/styles.css","src/images/chromeWindow.png","src/images/icon.png","src/images/logo.svg","src/images/tailwind.png","src/images/tailwind_bg.png","src/images/typescript.png","src/images/vue.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/content-vue/screenshot.png","icon":"examples/content-vue/src/images/logo.svg"},{"slug":"init","name":"init","title":"Initial","version":"1.0.0","manifest_version":3,"description":"A basic browser extension example built with Extension.js. A starting point for developers learning current browser extension tooling.","uiContext":[],"surfaces":[],"entrypoints":[],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":false,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","src/images/icon.png","src/images/javascript.png","src/index.js","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/init/screenshot.png","icon":"examples/init/src/images/icon.png"},{"slug":"javascript","name":"javascript","title":"JavaScript","version":"1.0.0","manifest_version":3,"description":"JavaScript-based extension with a sidebar panel. Adds a sidebar with a simple page.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/ContentApp.js","src/content/scripts.js","src/content/styles.css","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.js","src/sidebar/index.html","src/sidebar/scripts.js","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/javascript/screenshot.png","icon":"examples/javascript/src/images/icon.png"},{"slug":"new","name":"new","title":"Newtab","version":"1.0.0","manifest_version":3,"description":"New tab page example using plain JavaScript. Renders a simple page you can customize.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.js","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new/public/screenshot.png","icon":"examples/new/src/images/icon.png"},{"slug":"new-browser-flags","name":"new-browser-flags","title":"Newtab Browser Flags","version":"1.0.0","manifest_version":3,"description":"A browser extension example built with Extension.js demonstrating browser-specific features and flags. Shows how to handle browser differences across targets.","uiContext":["content","newtab","sidebar","background"],"surfaces":["content","newtab","sidebar","background"],"entrypoints":["newtab/index.html","sidebar/index.html","content/content.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{"build":"extension build"},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting","notifications"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","src/background.js","src/content/content.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/newtab.css","src/newtab/newtab.js","src/sidebar/index.html","src/sidebar/sidebar.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-browser-flags/screenshot.png","icon":"examples/new-browser-flags/src/images/icon.png"},{"slug":"new-config-eslint","name":"new-config-eslint","title":"Newtab Config ESLint","version":"1.0.0","manifest_version":3,"description":"New tab page example with ESLint configured. Shows how linting is set up in an extension project.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js","eslint.config.mjs"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-config-eslint/public/screenshot.png","icon":"examples/new-config-eslint/src/images/icon.png"},{"slug":"new-config-prettier","name":"new-config-prettier","title":"Newtab Config Prettier","version":"1.0.0","manifest_version":3,"description":"New tab page example with Prettier configured. Shows how formatting is set up in an extension project.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js",".prettierrc"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-config-prettier/public/screenshot.png","icon":"examples/new-config-prettier/src/images/icon.png"},{"slug":"new-config-stylelint","name":"new-config-stylelint","title":"Newtab Config Stylelint","version":"1.0.0","manifest_version":3,"description":"New tab page example with Stylelint configured. Shows how CSS linting is set up in an extension project.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"sass","configFiles":[".stylelintrc.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.js","src/newtab/styles.scss"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-config-stylelint/public/screenshot.png","icon":"examples/new-config-stylelint/src/images/icon.png"},{"slug":"new-crypto","name":"new-crypto","title":"Newtab Crypto","version":"1.0.0","manifest_version":3,"description":"New tab page example demonstrating basic crypto operations inside an extension.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-crypto/public/screenshot.png","icon":"examples/new-crypto/src/images/icon.png"},{"slug":"new-env","name":"new-env","title":"Environment Variables","version":"1.0.0","manifest_version":3,"description":"New tab page example demonstrating environment variables (.env) in an extension project.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"tags":["env","configuration","new-tab","javascript"],"difficulty":"beginner","timeToFirstSuccessMinutes":2,"firstSteps":["Copy .env.example to .env and edit the values","Run npm run dev to launch the extension","Open a new tab — the page reads injected EXTENSION_PUBLIC_* values"],"useCases":["Inject build-time configuration without leaking secrets","Switch behavior per browser, mode, or deployment target","Reference for safe env-var usage across JS, JSON, and HTML"],"docsUrl":"https://extension.js.org/docs/features/environment-variables","files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-env/public/screenshot.png","icon":"examples/new-env/src/images/icon.png"},{"slug":"new-less","name":"new-less","title":"Newtab Less","version":"1.0.0","manifest_version":3,"description":"New tab page example styled with Less. Renders a simple page and organizes styles with Less.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"less","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.js","src/newtab/styles.less"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-less/public/screenshot.png","icon":"examples/new-less/src/images/icon.png"},{"slug":"new-preact","name":"new-preact","title":"Newtab Preact","version":"1.0.0","manifest_version":3,"description":"New tab page example rendered with Preact. Loads a small Preact app you can extend.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"preact","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/NewTabApp.tsx","src/newtab/index.html","src/newtab/scripts.tsx","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-preact/public/screenshot.png","icon":"examples/new-preact/src/images/icon.png"},{"slug":"new-react","name":"new-react","title":"Newtab React","version":"1.0.0","manifest_version":3,"description":"A browser extension new tab page example built with Extension.js and React. Demonstrates a React-based new tab page with a working build and preview flow.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"react","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/NewTabApp.tsx","src/newtab/index.html","src/newtab/scripts.tsx","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-react/public/screenshot.png","icon":"examples/new-react/src/images/icon.png"},{"slug":"new-react-router","name":"new-react-router","title":"Newtab React Router","version":"1.0.0","manifest_version":3,"description":"New tab page example using React Router. Shows multiple routes inside the new tab app.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"react","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/NewTabApp.tsx","src/newtab/index.html","src/newtab/scripts.tsx","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-react-router/public/screenshot.png","icon":"examples/new-react-router/src/images/icon.png"},{"slug":"new-sass","name":"new-sass","title":"Newtab Sass","version":"1.0.0","manifest_version":3,"description":"New tab page example styled with Sass. Renders a simple page and organizes styles with Sass.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"sass","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.js","src/newtab/styles.scss"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-sass/public/screenshot.png","icon":"examples/new-sass/src/images/icon.png"},{"slug":"new-svelte","name":"new-svelte","title":"Newtab Svelte","version":"1.0.0","manifest_version":3,"description":"New tab page example rendered with Svelte. Loads a small Svelte app you can extend.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"svelte","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/NewTabApp.svelte","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-svelte/public/screenshot.png","icon":"examples/new-svelte/src/images/icon.png"},{"slug":"new-typescript","name":"new-typescript","title":"Newtab TypeScript","version":"1.0.0","manifest_version":3,"description":"A browser extension new tab page example built with Extension.js and TypeScript. Demonstrates a type-safe new tab page with a working build and preview flow.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-typescript/public/screenshot.png","icon":"examples/new-typescript/src/images/icon.png"},{"slug":"new-vue","name":"new-vue","title":"Newtab Vue","version":"1.0.0","manifest_version":3,"description":"New tab page example rendered with Vue. Loads a small Vue app you can extend.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html"],"uiFramework":"vue","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/newtab/NewTabApp.vue","src/newtab/index.html","src/newtab/scripts.ts","src/newtab/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/new-vue/public/screenshot.png","icon":"examples/new-vue/src/images/icon.png"},{"slug":"playwright","name":"playwright","title":"Playwright E2E Sidebar","version":"1.0.0","manifest_version":3,"description":"Sidebar extension example demonstrating Playwright-driven E2E automation.","uiContext":["sidebar","background"],"surfaces":["sidebar","background"],"entrypoints":["sidebar/index.html"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"tags":["testing","playwright","e2e","sidebar","typescript"],"difficulty":"intermediate","timeToFirstSuccessMinutes":4,"firstSteps":["Run npm install to set up Playwright deps","Run npm run dev to launch the extension","Run npm test to execute the Playwright spec against the loaded extension"],"useCases":["End-to-end testing for browser-extension surfaces (popup, sidebar, content)","CI-ready Playwright setup wired to Extension.js builds","Reference for automating extension flows in headed and headless modes"],"docsUrl":"https://extension.js.org/docs/workflows/playwright-e2e","files":["extension.config.js","public/screenshot.png","src/background.ts","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.ts","src/sidebar/index.html","src/sidebar/scripts.ts","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/playwright/public/screenshot.png","icon":"examples/playwright/src/images/icon.png"},{"slug":"preact","name":"preact","title":"Preact","version":"1.0.0","manifest_version":3,"description":"Preact-based extension with a sidebar panel. Adds a sidebar with a simple page.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.tsx"],"uiFramework":"preact","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/ContentApp.tsx","src/content/scripts.tsx","src/content/styles.css","src/images/icon.png","src/manifest.json","src/public/extension.svg","src/public/logo.svg","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/preact/public/screenshot.png","icon":"examples/preact/src/images/icon.png"},{"slug":"react","name":"react","title":"React","version":"1.0.0","manifest_version":3,"description":"React-based extension with a sidebar panel. Adds a sidebar with a simple page.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.tsx"],"uiFramework":"react","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/ContentApp.tsx","src/content/scripts.tsx","src/content/styles.css","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/react/public/screenshot.png","icon":"examples/react/src/images/icon.png"},{"slug":"sidebar","name":"sidebar","title":"Sidebar page","version":"1.0.0","manifest_version":3,"description":"Sidebar panel example. Adds a sidebar to the browser with a simple page.","uiContext":["sidebar","background"],"surfaces":["sidebar","background"],"entrypoints":["sidebar/index.html"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/sidebar/index.html","src/sidebar/scripts.js","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/sidebar/public/screenshot.png","icon":"examples/sidebar/src/images/icon.png"},{"slug":"sidebar-antd","name":"sidebar-antd","title":"Sidebar Ant Design","version":"1.0.0","manifest_version":3,"description":"React sidebar using antd and @ant-design/x. Regression coverage for issue #445.","uiContext":["sidebar","background"],"surfaces":["sidebar","background"],"entrypoints":["sidebar/index.html"],"uiFramework":"react","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.jsx","src/sidebar/index.html","src/sidebar/scripts.jsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/sidebar-antd/public/screenshot.png","icon":"examples/sidebar-antd/src/images/icon.png"},{"slug":"sidebar-monorepo-turbopack","name":"sidebar-monorepo-turbopack","title":"Sidebar Turbopack","version":"1.0.0","manifest_version":3,"description":"Monorepo example with content script and sidebar.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{"dev":"extension dev packages/extension","build":"extension build packages/extension"},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","packages/analytics/src/index.js","packages/extension/src/background.js","packages/extension/src/content/scripts.js","packages/extension/src/content/styles.css","packages/extension/src/images/icon.png","packages/extension/src/manifest.json","packages/extension/src/sidebar/SidebarApp.js","packages/extension/src/sidebar/index.html","packages/extension/src/sidebar/scripts.js","packages/extension/src/sidebar/styles.css","public/screenshot.png"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/sidebar-monorepo-turbopack/public/screenshot.png","icon":null},{"slug":"sidebar-shadcn","name":"sidebar-shadcn","title":"Sidebar shadcn","version":"1.0.0","manifest_version":3,"description":"React sidebar example using shadcn/ui components. Adds a sidebar panel with a simple React page.","uiContext":["sidebar","background"],"surfaces":["sidebar","background"],"entrypoints":["sidebar/index.html"],"uiFramework":"react","css":"css","configFiles":["postcss.config.js","tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/components/ui/button.tsx","src/components/ui/card.tsx","src/components/ui/label.tsx","src/components/ui/switch.tsx","src/images/icon.png","src/lib/utils.ts","src/manifest.json","src/sidebar/SidebarApp.tsx","src/sidebar/index.html","src/sidebar/scripts.tsx","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/sidebar-shadcn/public/screenshot.png","icon":"examples/sidebar-shadcn/src/images/icon.png"},{"slug":"special-folders-pages","name":"special-folders-pages","title":"Special Folders: pages/","version":"1.0.0","manifest_version":3,"description":"Opens a welcome page on extension load, showcasing the pages/ folder.","uiContext":["background"],"surfaces":["background"],"entrypoints":[],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"tags":["special-folders","pages","welcome-page","javascript"],"difficulty":"beginner","timeToFirstSuccessMinutes":2,"firstSteps":["Run npm run dev to launch the extension","A welcome page from src/pages/ opens automatically on install","Add another HTML file under src/pages/ — it ships as a separate page"],"useCases":["Onboarding, help, and welcome pages that ship with the extension","Standalone HTML routes without manifest plumbing","Reference for Extension.js\'s pages/ special folder convention"],"docsUrl":"https://extension.js.org/docs/features/special-folders","files":["extension.config.js","public/css/file.css","public/html/file.html","public/js/file.js","public/screenshot.png","src/background.js","src/images/icon.png","src/images/javascript.png","src/manifest.json","src/sandbox/index.html","src/sandbox/scripts.js","src/sandbox/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/special-folders-pages/public/screenshot.png","icon":"examples/special-folders-pages/src/images/icon.png"},{"slug":"special-folders-scripts","name":"special-folders-scripts","title":"Special Folders Scripts","version":"1.0.0","manifest_version":3,"description":"Demonstrates scripts/ folder organization and how to run standalone scripts via the extension.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["activeTab","scripting"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":false,"files":["extension.config.js","public/screenshot.png","src/background.js","src/content/scripts.js","src/content/styles.css","src/images/icon.png","src/images/javascript.png","src/manifest.json"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/special-folders-scripts/public/screenshot.png","icon":"examples/special-folders-scripts/src/images/icon.png"},{"slug":"svelte","name":"svelte","title":"Svelte","version":"1.0.0","manifest_version":3,"description":"Svelte-based extension with a sidebar panel. Adds a sidebar with a simple page.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"svelte","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/ContentApp.svelte","src/content/scripts.ts","src/content/styles.css","src/images/icon.png","src/manifest.json","src/public/extension.svg","src/sidebar/SidebarApp.svelte","src/sidebar/index.html","src/sidebar/scripts.ts","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/svelte/public/screenshot.png","icon":"examples/svelte/src/images/icon.png"},{"slug":"transformers-js","name":"transformers-js","title":"Transformers.js","version":"1.0.0","manifest_version":3,"description":"Transformers.js demo with a sidebar and a content script: classify the active page or your selection on-device via WebGPU/WASM.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.js","src/constants.js","src/content/scripts.js","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.js","src/sidebar/index.html","src/sidebar/sakura.css","src/sidebar/scripts.js","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/transformers-js/public/screenshot.png","icon":"examples/transformers-js/src/images/icon.png"},{"slug":"typescript","name":"typescript","title":"TypeScript","version":"1.0.0","manifest_version":3,"description":"TypeScript-based extension with a sidebar panel. Adds a sidebar with a simple page.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/ContentApp.ts","src/content/scripts.ts","src/content/styles.css","src/content/types.d.ts","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.ts","src/sidebar/index.html","src/sidebar/scripts.ts","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/typescript/screenshot.png","icon":"examples/typescript/src/images/icon.png"},{"slug":"vue","name":"vue","title":"Vue","version":"1.0.0","manifest_version":3,"description":"Vue.js-based extension with a sidebar panel. Adds a sidebar with a simple page.","uiContext":["content","sidebar","background"],"surfaces":["content","sidebar","background"],"entrypoints":["sidebar/index.html","content/scripts.ts"],"uiFramework":"vue","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":[],"host_permissions":[],"optional_permissions":[],"featured":true,"files":["extension.config.js","public/screenshot.png","src/background.ts","src/content/ContentApp.vue","src/content/scripts.ts","src/content/styles.css","src/content/types.d.ts","src/images/icon.png","src/manifest.json","src/sidebar/SidebarApp.vue","src/sidebar/index.html","src/sidebar/scripts.ts","src/sidebar/styles.css"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/vue/public/screenshot.png","icon":"examples/vue/src/images/icon.png"}]}');
|
|
1626
1632
|
const CACHE_DIR = node_path.join(node_os.homedir(), ".cache", "extension-js");
|
|
1627
1633
|
const CACHE_FILE = node_path.join(CACHE_DIR, "templates-meta.json");
|
|
@@ -1743,6 +1749,7 @@ async function searchTemplates(args) {
|
|
|
1743
1749
|
featured: t.featured,
|
|
1744
1750
|
useCases: t.useCases,
|
|
1745
1751
|
repositoryUrl: t.repositoryUrl,
|
|
1752
|
+
catalogUrl: templateCatalogUrl(t.slug),
|
|
1746
1753
|
downloads: t.downloads
|
|
1747
1754
|
}));
|
|
1748
1755
|
return envelope_envelope({
|
|
@@ -1776,7 +1783,8 @@ async function readTemplateSource(args) {
|
|
|
1776
1783
|
permissions: template.permissions,
|
|
1777
1784
|
patternExplanation: template.patternExplanation,
|
|
1778
1785
|
keyFiles: template.keyFiles,
|
|
1779
|
-
repositoryUrl: template.repositoryUrl
|
|
1786
|
+
repositoryUrl: template.repositoryUrl,
|
|
1787
|
+
catalogUrl: templateCatalogUrl(template.slug)
|
|
1780
1788
|
};
|
|
1781
1789
|
if (!args.files?.length) return envelope_envelope({
|
|
1782
1790
|
ok: true,
|
|
@@ -12269,6 +12277,7 @@ async function add_feature_handler(args) {
|
|
|
12269
12277
|
referenceTemplate: {
|
|
12270
12278
|
slug: templateSlug,
|
|
12271
12279
|
repositoryUrl: `${EXAMPLES_TREE_BASE}/${templateSlug}`,
|
|
12280
|
+
catalogUrl: templateCatalogUrl(templateSlug),
|
|
12272
12281
|
referenceFiles: referenceFiles.filter((f)=>f.includes(featureDir) || f.includes("manifest"))
|
|
12273
12282
|
},
|
|
12274
12283
|
manifestUpdates,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export declare const PINNED_COMMIT = "
|
|
1
|
+
export declare const PINNED_COMMIT = "52c0d871c433d9c54878767175ce8ffc9a951756";
|
|
2
|
+
export type TemplateCatalogUtmSource = "mcp" | "mcp-create";
|
|
3
|
+
export declare function templateCatalogUrl(slug: string, utmSource?: TemplateCatalogUtmSource): string;
|
|
2
4
|
export declare function templateMetaUrls(): Promise<string[]>;
|
|
3
5
|
export declare function stripTemplatePathPrefix(slug: string, relativePath: string): string;
|
|
4
6
|
export declare function templateFileUrls(slug: string, relativePath: string): Promise<string[]>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.4.0",
|
|
5
5
|
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 28 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, Firefox, Safari, and every Chromium- or Gecko-based browser (Brave, Opera, Vivaldi, Yandex, Waterfox, LibreWolf). Powered by extension.dev and Extension.js.",
|
|
6
6
|
"mcpName": "io.github.extensiondev/mcp",
|
|
7
7
|
"license": "Apache-2.0",
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://extension.dev",
|
|
10
|
-
"version": "10.
|
|
10
|
+
"version": "10.4.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@extension.dev/mcp",
|
|
16
|
-
"version": "10.
|
|
16
|
+
"version": "10.4.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|