@extension.dev/mcp 10.4.2 → 10.4.3
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 +22 -0
- package/README.md +1 -1
- package/dist/module.js +188 -441
- package/dist/rslib-runtime.js +0 -2
- package/dist/src/tools/add-feature.d.ts +1 -0
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/module.js
CHANGED
|
@@ -17,397 +17,10 @@ import { filterKeysForThisBrowser } from "browser-extension-manifest-fields";
|
|
|
17
17
|
import node_net from "node:net";
|
|
18
18
|
import { promisify } from "node:util";
|
|
19
19
|
import { extensionInstall, extensionUninstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
20
|
-
__webpack_require__.add({
|
|
21
|
-
"./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/origins.cjs" (__unused_rspack_module, exports) {
|
|
22
|
-
var __nested_rspack_require_18_37__ = {};
|
|
23
|
-
(()=>{
|
|
24
|
-
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
25
|
-
for(var key in definition)if (__nested_rspack_require_18_37__.o(definition, key) && !__nested_rspack_require_18_37__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
get: definition[key]
|
|
28
|
-
});
|
|
29
|
-
};
|
|
30
|
-
})();
|
|
31
|
-
(()=>{
|
|
32
|
-
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
33
|
-
})();
|
|
34
|
-
(()=>{
|
|
35
|
-
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
36
|
-
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
37
|
-
value: 'Module'
|
|
38
|
-
});
|
|
39
|
-
Object.defineProperty(exports1, '__esModule', {
|
|
40
|
-
value: true
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
})();
|
|
44
|
-
var __nested_rspack_exports__ = {};
|
|
45
|
-
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
46
|
-
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
47
|
-
DEV_LOCALHOST_ORIGINS: ()=>DEV_LOCALHOST_ORIGINS,
|
|
48
|
-
PROD_ORIGINS: ()=>PROD_ORIGINS,
|
|
49
|
-
isLocalOrigin: ()=>isLocalOrigin,
|
|
50
|
-
resolveOrigins: ()=>resolveOrigins
|
|
51
|
-
});
|
|
52
|
-
const PROD_ORIGINS = {
|
|
53
|
-
www: "https://www.extension.dev",
|
|
54
|
-
console: "https://console.extension.dev",
|
|
55
|
-
inspect: "https://inspect.extension.dev",
|
|
56
|
-
preview: "https://preview.extension.dev",
|
|
57
|
-
templates: "https://templates.extension.dev",
|
|
58
|
-
intelligence: "https://intelligence.extension.dev",
|
|
59
|
-
userland: "https://extension.dev",
|
|
60
|
-
registry: "https://registry.extension.land",
|
|
61
|
-
media: "https://media.extension.land"
|
|
62
|
-
};
|
|
63
|
-
const DEV_LOCALHOST_ORIGINS = {
|
|
64
|
-
www: "http://localhost:3100",
|
|
65
|
-
console: "http://console.extension.localhost",
|
|
66
|
-
inspect: "http://inspect.extension.localhost",
|
|
67
|
-
preview: "http://preview.extension.localhost",
|
|
68
|
-
templates: "http://templates.extension.localhost",
|
|
69
|
-
intelligence: "http://intelligence.extension.localhost",
|
|
70
|
-
userland: "http://userland.extension.localhost",
|
|
71
|
-
registry: "https://registry.extension.land",
|
|
72
|
-
media: "https://media.extension.land"
|
|
73
|
-
};
|
|
74
|
-
function strip(value) {
|
|
75
|
-
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
76
|
-
}
|
|
77
|
-
function isLocalOrigin(url) {
|
|
78
|
-
const raw = strip(url);
|
|
79
|
-
if (!raw) return false;
|
|
80
|
-
let host;
|
|
81
|
-
try {
|
|
82
|
-
host = new URL(raw).hostname;
|
|
83
|
-
} catch {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
87
|
-
}
|
|
88
|
-
function resolveOrigins(overrides = {}, opts = {}) {
|
|
89
|
-
const devLike = isLocalOrigin(overrides.www) || isLocalOrigin(overrides.console) || isLocalOrigin(opts.hint);
|
|
90
|
-
const base = devLike ? DEV_LOCALHOST_ORIGINS : PROD_ORIGINS;
|
|
91
|
-
const pick = (key)=>strip(overrides[key]) || base[key];
|
|
92
|
-
return {
|
|
93
|
-
www: pick("www"),
|
|
94
|
-
console: pick("console"),
|
|
95
|
-
inspect: pick("inspect"),
|
|
96
|
-
preview: pick("preview"),
|
|
97
|
-
templates: pick("templates"),
|
|
98
|
-
intelligence: pick("intelligence"),
|
|
99
|
-
userland: pick("userland"),
|
|
100
|
-
registry: pick("registry"),
|
|
101
|
-
media: pick("media")
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
exports.DEV_LOCALHOST_ORIGINS = __nested_rspack_exports__.DEV_LOCALHOST_ORIGINS;
|
|
105
|
-
exports.PROD_ORIGINS = __nested_rspack_exports__.PROD_ORIGINS;
|
|
106
|
-
exports.isLocalOrigin = __nested_rspack_exports__.isLocalOrigin;
|
|
107
|
-
exports.resolveOrigins = __nested_rspack_exports__.resolveOrigins;
|
|
108
|
-
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
109
|
-
"DEV_LOCALHOST_ORIGINS",
|
|
110
|
-
"PROD_ORIGINS",
|
|
111
|
-
"isLocalOrigin",
|
|
112
|
-
"resolveOrigins"
|
|
113
|
-
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
114
|
-
Object.defineProperty(exports, "__esModule", {
|
|
115
|
-
value: true
|
|
116
|
-
});
|
|
117
|
-
},
|
|
118
|
-
"./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/paths.cjs" (__unused_rspack_module, exports) {
|
|
119
|
-
var __nested_rspack_require_18_37__ = {};
|
|
120
|
-
(()=>{
|
|
121
|
-
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
122
|
-
for(var key in definition)if (__nested_rspack_require_18_37__.o(definition, key) && !__nested_rspack_require_18_37__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
123
|
-
enumerable: true,
|
|
124
|
-
get: definition[key]
|
|
125
|
-
});
|
|
126
|
-
};
|
|
127
|
-
})();
|
|
128
|
-
(()=>{
|
|
129
|
-
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
130
|
-
})();
|
|
131
|
-
(()=>{
|
|
132
|
-
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
133
|
-
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
134
|
-
value: 'Module'
|
|
135
|
-
});
|
|
136
|
-
Object.defineProperty(exports1, '__esModule', {
|
|
137
|
-
value: true
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
})();
|
|
141
|
-
var __nested_rspack_exports__ = {};
|
|
142
|
-
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
143
|
-
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
144
|
-
ConsoleProjectPage: ()=>ConsoleProjectPage,
|
|
145
|
-
consoleProjectPath: ()=>consoleProjectPath,
|
|
146
|
-
consoleWorkspacePath: ()=>consoleWorkspacePath,
|
|
147
|
-
inspectTabPath: ()=>inspectTabPath,
|
|
148
|
-
previewSharePath: ()=>previewSharePath,
|
|
149
|
-
templateTabPath: ()=>templateTabPath,
|
|
150
|
-
withQuery: ()=>withQuery,
|
|
151
|
-
wwwDevicePath: ()=>wwwDevicePath,
|
|
152
|
-
wwwImportPath: ()=>wwwImportPath,
|
|
153
|
-
wwwNewPath: ()=>wwwNewPath,
|
|
154
|
-
wwwTemplatesPath: ()=>wwwTemplatesPath
|
|
155
|
-
});
|
|
156
|
-
const seg = (value)=>encodeURIComponent(String(value));
|
|
157
|
-
function join(base, sub) {
|
|
158
|
-
if (!sub) return base;
|
|
159
|
-
return `${base}/${sub.replace(/^\/+/, "")}`;
|
|
160
|
-
}
|
|
161
|
-
function consoleWorkspacePath(workspace, page = "") {
|
|
162
|
-
return join(`/${seg(workspace)}`, page);
|
|
163
|
-
}
|
|
164
|
-
function consoleProjectPath(ref, page = "") {
|
|
165
|
-
return join(`/${seg(ref.workspace)}/${seg(ref.project)}`, page);
|
|
166
|
-
}
|
|
167
|
-
const ConsoleProjectPage = {
|
|
168
|
-
overview: "",
|
|
169
|
-
onboard: "onboard",
|
|
170
|
-
activity: "activity",
|
|
171
|
-
builds: "builds",
|
|
172
|
-
build: (buildId, browser)=>browser ? `builds/${seg(buildId)}/${seg(browser)}` : `builds/${seg(buildId)}`,
|
|
173
|
-
releases: "releases",
|
|
174
|
-
releasesNew: "releases/new",
|
|
175
|
-
release: (releaseId)=>`releases/${seg(releaseId)}`,
|
|
176
|
-
stores: "stores",
|
|
177
|
-
storesNew: "stores/new",
|
|
178
|
-
store: (store)=>`stores/${seg(store)}`,
|
|
179
|
-
storeSubmissions: (store)=>`stores/${seg(store)}/submissions`,
|
|
180
|
-
storeSubmissionNew: (store)=>`stores/${seg(store)}/submissions/new`,
|
|
181
|
-
storeSubmission: (store, submissionId)=>`stores/${seg(store)}/submissions/${seg(submissionId)}`,
|
|
182
|
-
projectSettings: "project-settings",
|
|
183
|
-
projectSettingsSection: (section)=>`project-settings/${seg(section)}`,
|
|
184
|
-
accessTokens: "settings/access-tokens"
|
|
185
|
-
};
|
|
186
|
-
function withQuery(path, query) {
|
|
187
|
-
if (!query) return path;
|
|
188
|
-
const params = new URLSearchParams();
|
|
189
|
-
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
190
|
-
const qs = params.toString();
|
|
191
|
-
return qs ? `${path}?${qs}` : path;
|
|
192
|
-
}
|
|
193
|
-
function wwwNewPath(query) {
|
|
194
|
-
return withQuery("/new", query);
|
|
195
|
-
}
|
|
196
|
-
function wwwImportPath(query) {
|
|
197
|
-
return withQuery("/import", query);
|
|
198
|
-
}
|
|
199
|
-
function wwwDevicePath() {
|
|
200
|
-
return "/device";
|
|
201
|
-
}
|
|
202
|
-
function wwwTemplatesPath(slug) {
|
|
203
|
-
return slug ? `/templates/${seg(slug)}` : "/templates";
|
|
204
|
-
}
|
|
205
|
-
function templateTabPath(slug, tab = "preview") {
|
|
206
|
-
return "preview" === tab ? `/${seg(slug)}` : `/${seg(slug)}/${tab}`;
|
|
207
|
-
}
|
|
208
|
-
function inspectTabPath(tab = "preview") {
|
|
209
|
-
return "preview" === tab ? "/" : `/${tab}`;
|
|
210
|
-
}
|
|
211
|
-
function previewSharePath(previewId) {
|
|
212
|
-
return `/?preview=${seg(previewId)}`;
|
|
213
|
-
}
|
|
214
|
-
exports.ConsoleProjectPage = __nested_rspack_exports__.ConsoleProjectPage;
|
|
215
|
-
exports.consoleProjectPath = __nested_rspack_exports__.consoleProjectPath;
|
|
216
|
-
exports.consoleWorkspacePath = __nested_rspack_exports__.consoleWorkspacePath;
|
|
217
|
-
exports.inspectTabPath = __nested_rspack_exports__.inspectTabPath;
|
|
218
|
-
exports.previewSharePath = __nested_rspack_exports__.previewSharePath;
|
|
219
|
-
exports.templateTabPath = __nested_rspack_exports__.templateTabPath;
|
|
220
|
-
exports.withQuery = __nested_rspack_exports__.withQuery;
|
|
221
|
-
exports.wwwDevicePath = __nested_rspack_exports__.wwwDevicePath;
|
|
222
|
-
exports.wwwImportPath = __nested_rspack_exports__.wwwImportPath;
|
|
223
|
-
exports.wwwNewPath = __nested_rspack_exports__.wwwNewPath;
|
|
224
|
-
exports.wwwTemplatesPath = __nested_rspack_exports__.wwwTemplatesPath;
|
|
225
|
-
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
226
|
-
"ConsoleProjectPage",
|
|
227
|
-
"consoleProjectPath",
|
|
228
|
-
"consoleWorkspacePath",
|
|
229
|
-
"inspectTabPath",
|
|
230
|
-
"previewSharePath",
|
|
231
|
-
"templateTabPath",
|
|
232
|
-
"withQuery",
|
|
233
|
-
"wwwDevicePath",
|
|
234
|
-
"wwwImportPath",
|
|
235
|
-
"wwwNewPath",
|
|
236
|
-
"wwwTemplatesPath"
|
|
237
|
-
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
238
|
-
Object.defineProperty(exports, "__esModule", {
|
|
239
|
-
value: true
|
|
240
|
-
});
|
|
241
|
-
},
|
|
242
|
-
"./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/userland.cjs" (__unused_rspack_module, exports) {
|
|
243
|
-
var __nested_rspack_require_18_37__ = {};
|
|
244
|
-
(()=>{
|
|
245
|
-
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
246
|
-
for(var key in definition)if (__nested_rspack_require_18_37__.o(definition, key) && !__nested_rspack_require_18_37__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
247
|
-
enumerable: true,
|
|
248
|
-
get: definition[key]
|
|
249
|
-
});
|
|
250
|
-
};
|
|
251
|
-
})();
|
|
252
|
-
(()=>{
|
|
253
|
-
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
254
|
-
})();
|
|
255
|
-
(()=>{
|
|
256
|
-
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
257
|
-
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
258
|
-
value: 'Module'
|
|
259
|
-
});
|
|
260
|
-
Object.defineProperty(exports1, '__esModule', {
|
|
261
|
-
value: true
|
|
262
|
-
});
|
|
263
|
-
};
|
|
264
|
-
})();
|
|
265
|
-
var __nested_rspack_exports__ = {};
|
|
266
|
-
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
267
|
-
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
268
|
-
userlandBuildUrl: ()=>userlandBuildUrl,
|
|
269
|
-
USERLAND_BUILD_TABS: ()=>USERLAND_BUILD_TABS,
|
|
270
|
-
userlandUrl: ()=>userlandUrl,
|
|
271
|
-
UserlandProjectPage: ()=>UserlandProjectPage,
|
|
272
|
-
UserlandDialog: ()=>UserlandDialog,
|
|
273
|
-
userlandChannelUrl: ()=>userlandChannelUrl,
|
|
274
|
-
userlandProjectPath: ()=>userlandProjectPath,
|
|
275
|
-
isUserlandBuildTab: ()=>isUserlandBuildTab,
|
|
276
|
-
userlandDialogUrl: ()=>userlandDialogUrl,
|
|
277
|
-
userlandHostMode: ()=>userlandHostMode,
|
|
278
|
-
userlandOrigin: ()=>userlandOrigin
|
|
279
|
-
});
|
|
280
|
-
const PROD_ORIGINS = {
|
|
281
|
-
www: "https://www.extension.dev",
|
|
282
|
-
console: "https://console.extension.dev",
|
|
283
|
-
inspect: "https://inspect.extension.dev",
|
|
284
|
-
preview: "https://preview.extension.dev",
|
|
285
|
-
templates: "https://templates.extension.dev",
|
|
286
|
-
intelligence: "https://intelligence.extension.dev",
|
|
287
|
-
userland: "https://extension.dev",
|
|
288
|
-
registry: "https://registry.extension.land",
|
|
289
|
-
media: "https://media.extension.land"
|
|
290
|
-
};
|
|
291
|
-
function strip(value) {
|
|
292
|
-
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
293
|
-
}
|
|
294
|
-
function isLocalOrigin(url) {
|
|
295
|
-
const raw = strip(url);
|
|
296
|
-
if (!raw) return false;
|
|
297
|
-
let host;
|
|
298
|
-
try {
|
|
299
|
-
host = new URL(raw).hostname;
|
|
300
|
-
} catch {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
304
|
-
}
|
|
305
|
-
function withQuery(path, query) {
|
|
306
|
-
if (!query) return path;
|
|
307
|
-
const params = new URLSearchParams();
|
|
308
|
-
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
309
|
-
const qs = params.toString();
|
|
310
|
-
return qs ? `${path}?${qs}` : path;
|
|
311
|
-
}
|
|
312
|
-
const userland_seg = (value)=>encodeURIComponent(String(value));
|
|
313
|
-
const USERLAND_BUILD_TABS = [
|
|
314
|
-
"preview",
|
|
315
|
-
"whats-new",
|
|
316
|
-
"usage"
|
|
317
|
-
];
|
|
318
|
-
function isUserlandBuildTab(value) {
|
|
319
|
-
return USERLAND_BUILD_TABS.includes(value);
|
|
320
|
-
}
|
|
321
|
-
const UserlandDialog = {
|
|
322
|
-
run: "run",
|
|
323
|
-
integrity: "integrity"
|
|
324
|
-
};
|
|
325
|
-
const DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
326
|
-
function userland_strip(value) {
|
|
327
|
-
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
328
|
-
}
|
|
329
|
-
function userlandHostMode(base) {
|
|
330
|
-
return isLocalOrigin(userland_strip(base) || PROD_ORIGINS.userland) ? "path" : "subdomain";
|
|
331
|
-
}
|
|
332
|
-
function userlandOrigin(workspace, options = {}) {
|
|
333
|
-
const base = userland_strip(options.base) || PROD_ORIGINS.userland;
|
|
334
|
-
if ("path" === userlandHostMode(base)) return base;
|
|
335
|
-
const slug = String(workspace ?? "").trim().toLowerCase();
|
|
336
|
-
if (!DNS_LABEL.test(slug)) throw new Error(`Workspace slug ${JSON.stringify(workspace)} cannot be a subdomain label; userland production hosts are <workspace>.extension.dev.`);
|
|
337
|
-
const url = new URL(base);
|
|
338
|
-
url.hostname = `${slug}.${url.hostname}`;
|
|
339
|
-
return userland_strip(url.toString());
|
|
340
|
-
}
|
|
341
|
-
const UserlandProjectPage = {
|
|
342
|
-
overview: "",
|
|
343
|
-
build: (buildId)=>`builds/${userland_seg(buildId)}`,
|
|
344
|
-
buildTab: (buildId, tab = "preview")=>"preview" === tab ? `builds/${userland_seg(buildId)}` : `builds/${userland_seg(buildId)}/${tab}`,
|
|
345
|
-
browserBuild: (buildId, browser)=>`builds/${userland_seg(buildId)}/${userland_seg(browser)}`,
|
|
346
|
-
channel: (channel)=>`channels/${userland_seg(channel)}`,
|
|
347
|
-
channelShortcut: (channel)=>userland_seg(channel),
|
|
348
|
-
channelBrowser: (browser, channel)=>`channels/${userland_seg(browser)}/${userland_seg(channel)}`,
|
|
349
|
-
version: (version)=>`versions/${userland_seg(version)}`,
|
|
350
|
-
versionBrowser: (browser, version)=>`versions/${userland_seg(browser)}/${userland_seg(version)}`
|
|
351
|
-
};
|
|
352
|
-
function userlandProjectPath(ref, page = "", options = {}) {
|
|
353
|
-
const head = "subdomain" === userlandHostMode(options.base) ? `/${userland_seg(ref.project)}` : `/${userland_seg(ref.workspace)}/${userland_seg(ref.project)}`;
|
|
354
|
-
const tail = String(page ?? "").replace(/^\/+/, "");
|
|
355
|
-
return tail ? `${head}/${tail}` : head;
|
|
356
|
-
}
|
|
357
|
-
function userlandUrl(ref, page = "", options = {}) {
|
|
358
|
-
const origin = userlandOrigin(ref.workspace, options);
|
|
359
|
-
const path = userlandProjectPath(ref, page, options);
|
|
360
|
-
return `${origin}${withQuery(path, options.query)}`;
|
|
361
|
-
}
|
|
362
|
-
function userlandBuildUrl(ref, buildId, options = {}) {
|
|
363
|
-
const page = options.browser ? UserlandProjectPage.browserBuild(buildId, options.browser) : UserlandProjectPage.build(buildId);
|
|
364
|
-
return userlandUrl(ref, page, options);
|
|
365
|
-
}
|
|
366
|
-
function userlandDialogUrl(ref, buildId, browser, dialog, options = {}) {
|
|
367
|
-
return userlandUrl(ref, UserlandProjectPage.browserBuild(buildId, browser), {
|
|
368
|
-
...options,
|
|
369
|
-
query: {
|
|
370
|
-
...options.query ?? {},
|
|
371
|
-
dialog
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
function userlandChannelUrl(ref, channel, options = {}) {
|
|
376
|
-
const page = options.browser ? UserlandProjectPage.channelBrowser(options.browser, channel) : options.shortcut ? UserlandProjectPage.channelShortcut(channel) : UserlandProjectPage.channel(channel);
|
|
377
|
-
return userlandUrl(ref, page, options);
|
|
378
|
-
}
|
|
379
|
-
exports.USERLAND_BUILD_TABS = __nested_rspack_exports__.USERLAND_BUILD_TABS;
|
|
380
|
-
exports.UserlandDialog = __nested_rspack_exports__.UserlandDialog;
|
|
381
|
-
exports.UserlandProjectPage = __nested_rspack_exports__.UserlandProjectPage;
|
|
382
|
-
exports.isUserlandBuildTab = __nested_rspack_exports__.isUserlandBuildTab;
|
|
383
|
-
exports.userlandBuildUrl = __nested_rspack_exports__.userlandBuildUrl;
|
|
384
|
-
exports.userlandChannelUrl = __nested_rspack_exports__.userlandChannelUrl;
|
|
385
|
-
exports.userlandDialogUrl = __nested_rspack_exports__.userlandDialogUrl;
|
|
386
|
-
exports.userlandHostMode = __nested_rspack_exports__.userlandHostMode;
|
|
387
|
-
exports.userlandOrigin = __nested_rspack_exports__.userlandOrigin;
|
|
388
|
-
exports.userlandProjectPath = __nested_rspack_exports__.userlandProjectPath;
|
|
389
|
-
exports.userlandUrl = __nested_rspack_exports__.userlandUrl;
|
|
390
|
-
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
391
|
-
"USERLAND_BUILD_TABS",
|
|
392
|
-
"UserlandDialog",
|
|
393
|
-
"UserlandProjectPage",
|
|
394
|
-
"isUserlandBuildTab",
|
|
395
|
-
"userlandBuildUrl",
|
|
396
|
-
"userlandChannelUrl",
|
|
397
|
-
"userlandDialogUrl",
|
|
398
|
-
"userlandHostMode",
|
|
399
|
-
"userlandOrigin",
|
|
400
|
-
"userlandProjectPath",
|
|
401
|
-
"userlandUrl"
|
|
402
|
-
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
403
|
-
Object.defineProperty(exports, "__esModule", {
|
|
404
|
-
value: true
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
20
|
var add_feature_namespaceObject = {};
|
|
409
21
|
__webpack_require__.r(add_feature_namespaceObject);
|
|
410
22
|
__webpack_require__.d(add_feature_namespaceObject, {
|
|
23
|
+
FEATURE_TEMPLATE_MAP: ()=>FEATURE_TEMPLATE_MAP,
|
|
411
24
|
handler: ()=>add_feature_handler,
|
|
412
25
|
schema: ()=>add_feature_schema
|
|
413
26
|
});
|
|
@@ -582,7 +195,7 @@ __webpack_require__.d(wait_namespaceObject, {
|
|
|
582
195
|
handler: ()=>wait_handler,
|
|
583
196
|
schema: ()=>wait_schema
|
|
584
197
|
});
|
|
585
|
-
var package_namespaceObject = JSON.parse('{"rE":"10.4.
|
|
198
|
+
var package_namespaceObject = JSON.parse('{"rE":"10.4.3","El":{"OP":"4.0.30"}}');
|
|
586
199
|
const ENVELOPE_SCHEMA = 1;
|
|
587
200
|
const collectWarnings = (warnings)=>{
|
|
588
201
|
if (!warnings) return [];
|
|
@@ -856,8 +469,65 @@ function readValidCredentials(nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
|
856
469
|
if (creds.expiresAt && creds.expiresAt <= nowSeconds) return null;
|
|
857
470
|
return creds;
|
|
858
471
|
}
|
|
859
|
-
const
|
|
860
|
-
|
|
472
|
+
const PROD_ORIGINS = {
|
|
473
|
+
www: "https://www.extension.dev",
|
|
474
|
+
console: "https://console.extension.dev",
|
|
475
|
+
docs: "https://docs.extension.dev",
|
|
476
|
+
inspect: "https://inspect.extension.dev",
|
|
477
|
+
preview: "https://preview.extension.dev",
|
|
478
|
+
templates: "https://templates.extension.dev",
|
|
479
|
+
themes: "https://themes.extension.dev",
|
|
480
|
+
code: "https://code.extension.dev",
|
|
481
|
+
userland: "https://extension.dev",
|
|
482
|
+
registry: "https://registry.extension.land",
|
|
483
|
+
media: "https://media.extension.land"
|
|
484
|
+
};
|
|
485
|
+
const DEV_LOCALHOST_ORIGINS = {
|
|
486
|
+
www: "http://localhost:3100",
|
|
487
|
+
console: "http://console.extension.localhost",
|
|
488
|
+
docs: "http://docs.extension.localhost",
|
|
489
|
+
inspect: "http://inspect.extension.localhost",
|
|
490
|
+
preview: "http://preview.extension.localhost",
|
|
491
|
+
templates: "http://templates.extension.localhost",
|
|
492
|
+
themes: "http://themes.extension.localhost",
|
|
493
|
+
code: "http://code.extension.localhost",
|
|
494
|
+
userland: "http://userland.extension.localhost",
|
|
495
|
+
registry: "https://registry.extension.land",
|
|
496
|
+
media: "https://media.extension.land"
|
|
497
|
+
};
|
|
498
|
+
function strip(value) {
|
|
499
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
500
|
+
}
|
|
501
|
+
function isLocalOrigin(url) {
|
|
502
|
+
const raw = strip(url);
|
|
503
|
+
if (!raw) return false;
|
|
504
|
+
let host;
|
|
505
|
+
try {
|
|
506
|
+
host = new URL(raw).hostname;
|
|
507
|
+
} catch {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
511
|
+
}
|
|
512
|
+
function resolveOrigins(overrides = {}, opts = {}) {
|
|
513
|
+
const devLike = isLocalOrigin(overrides.www) || isLocalOrigin(overrides.console) || isLocalOrigin(opts.hint);
|
|
514
|
+
const base = devLike ? DEV_LOCALHOST_ORIGINS : PROD_ORIGINS;
|
|
515
|
+
const pick = (key)=>strip(overrides[key]) || base[key];
|
|
516
|
+
return {
|
|
517
|
+
www: pick("www"),
|
|
518
|
+
console: pick("console"),
|
|
519
|
+
docs: pick("docs"),
|
|
520
|
+
inspect: pick("inspect"),
|
|
521
|
+
preview: pick("preview"),
|
|
522
|
+
templates: pick("templates"),
|
|
523
|
+
themes: pick("themes"),
|
|
524
|
+
code: pick("code"),
|
|
525
|
+
userland: pick("userland"),
|
|
526
|
+
registry: pick("registry"),
|
|
527
|
+
media: pick("media")
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
const DEFAULT_API = PROD_ORIGINS.www;
|
|
861
531
|
function tokenTtlNote(workspaceSlug, projectSlug) {
|
|
862
532
|
const tokensUrl = workspaceSlug && projectSlug ? consoleProjectUrl({
|
|
863
533
|
workspace: workspaceSlug,
|
|
@@ -1165,12 +835,100 @@ function withAccessToken(url, token) {
|
|
|
1165
835
|
return url;
|
|
1166
836
|
}
|
|
1167
837
|
}
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
838
|
+
const seg = (value)=>encodeURIComponent(String(value));
|
|
839
|
+
function paths_join(base, sub) {
|
|
840
|
+
if (!sub) return base;
|
|
841
|
+
return `${base}/${sub.replace(/^\/+/, "")}`;
|
|
842
|
+
}
|
|
843
|
+
function consoleProjectPath(ref, page = "") {
|
|
844
|
+
return paths_join(`/${seg(ref.workspace)}/${seg(ref.project)}`, page);
|
|
845
|
+
}
|
|
846
|
+
function withQuery(path, query) {
|
|
847
|
+
if (!query) return path;
|
|
848
|
+
const params = new URLSearchParams();
|
|
849
|
+
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
850
|
+
const qs = params.toString();
|
|
851
|
+
return qs ? `${path}?${qs}` : path;
|
|
852
|
+
}
|
|
853
|
+
function wwwNewPath(query) {
|
|
854
|
+
return withQuery("/new", query);
|
|
855
|
+
}
|
|
856
|
+
const userland_PROD_ORIGINS = {
|
|
857
|
+
www: "https://www.extension.dev",
|
|
858
|
+
console: "https://console.extension.dev",
|
|
859
|
+
docs: "https://docs.extension.dev",
|
|
860
|
+
inspect: "https://inspect.extension.dev",
|
|
861
|
+
preview: "https://preview.extension.dev",
|
|
862
|
+
templates: "https://templates.extension.dev",
|
|
863
|
+
themes: "https://themes.extension.dev",
|
|
864
|
+
code: "https://code.extension.dev",
|
|
865
|
+
userland: "https://extension.dev",
|
|
866
|
+
registry: "https://registry.extension.land",
|
|
867
|
+
media: "https://media.extension.land"
|
|
868
|
+
};
|
|
869
|
+
function userland_strip(value) {
|
|
870
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
871
|
+
}
|
|
872
|
+
function userland_isLocalOrigin(url) {
|
|
873
|
+
const raw = userland_strip(url);
|
|
874
|
+
if (!raw) return false;
|
|
875
|
+
let host;
|
|
876
|
+
try {
|
|
877
|
+
host = new URL(raw).hostname;
|
|
878
|
+
} catch {
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
882
|
+
}
|
|
883
|
+
function userland_withQuery(path, query) {
|
|
884
|
+
if (!query) return path;
|
|
885
|
+
const params = new URLSearchParams();
|
|
886
|
+
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
887
|
+
const qs = params.toString();
|
|
888
|
+
return qs ? `${path}?${qs}` : path;
|
|
889
|
+
}
|
|
890
|
+
const userland_seg = (value)=>encodeURIComponent(String(value));
|
|
891
|
+
const DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
892
|
+
function userland_userland_strip(value) {
|
|
893
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
894
|
+
}
|
|
895
|
+
function userlandHostMode(base) {
|
|
896
|
+
return userland_isLocalOrigin(userland_userland_strip(base) || userland_PROD_ORIGINS.userland) ? "path" : "subdomain";
|
|
897
|
+
}
|
|
898
|
+
function userlandOrigin(workspace, options = {}) {
|
|
899
|
+
const base = userland_userland_strip(options.base) || userland_PROD_ORIGINS.userland;
|
|
900
|
+
if ("path" === userlandHostMode(base)) return base;
|
|
901
|
+
const slug = String(workspace ?? "").trim().toLowerCase();
|
|
902
|
+
if (!DNS_LABEL.test(slug)) throw new Error(`Workspace slug ${JSON.stringify(workspace)} cannot be a subdomain label; userland production hosts are <workspace>.extension.dev.`);
|
|
903
|
+
const url = new URL(base);
|
|
904
|
+
url.hostname = `${slug}.${url.hostname}`;
|
|
905
|
+
return userland_userland_strip(url.toString());
|
|
906
|
+
}
|
|
907
|
+
const UserlandProjectPage = {
|
|
908
|
+
overview: "",
|
|
909
|
+
build: (buildId)=>`builds/${userland_seg(buildId)}`,
|
|
910
|
+
buildTab: (buildId, tab = "preview")=>"preview" === tab ? `builds/${userland_seg(buildId)}` : `builds/${userland_seg(buildId)}/${tab}`,
|
|
911
|
+
browserBuild: (buildId, browser)=>`builds/${userland_seg(buildId)}/${userland_seg(browser)}`,
|
|
912
|
+
channel: (channel)=>`channels/${userland_seg(channel)}`,
|
|
913
|
+
channelShortcut: (channel)=>userland_seg(channel),
|
|
914
|
+
channelBrowser: (browser, channel)=>`channels/${userland_seg(browser)}/${userland_seg(channel)}`,
|
|
915
|
+
version: (version)=>`versions/${userland_seg(version)}`,
|
|
916
|
+
versionBrowser: (browser, version)=>`versions/${userland_seg(browser)}/${userland_seg(version)}`
|
|
917
|
+
};
|
|
918
|
+
function userlandProjectPath(ref, page = "", options = {}) {
|
|
919
|
+
const head = "subdomain" === userlandHostMode(options.base) ? `/${userland_seg(ref.project)}` : `/${userland_seg(ref.workspace)}/${userland_seg(ref.project)}`;
|
|
920
|
+
const tail = String(page ?? "").replace(/^\/+/, "");
|
|
921
|
+
return tail ? `${head}/${tail}` : head;
|
|
922
|
+
}
|
|
923
|
+
function userlandUrl(ref, page = "", options = {}) {
|
|
924
|
+
const origin = userlandOrigin(ref.workspace, options);
|
|
925
|
+
const path = userlandProjectPath(ref, page, options);
|
|
926
|
+
return `${origin}${userland_withQuery(path, options.query)}`;
|
|
927
|
+
}
|
|
928
|
+
PROD_ORIGINS.registry;
|
|
1171
929
|
function mcpOrigins(apiHint) {
|
|
1172
930
|
const www = String(apiHint || process.env.EXTENSION_DEV_API_URL || "").trim() || void 0;
|
|
1173
|
-
return
|
|
931
|
+
return resolveOrigins({
|
|
1174
932
|
www,
|
|
1175
933
|
console: process.env.EXTENSION_DEV_CONSOLE_URL,
|
|
1176
934
|
inspect: process.env.EXTENSION_DEV_INSPECT_URL,
|
|
@@ -1210,12 +968,12 @@ function registryFileUrl(ref, file) {
|
|
|
1210
968
|
function consoleProjectUrl(ref, page, apiHint) {
|
|
1211
969
|
const base = consoleBase(apiHint);
|
|
1212
970
|
if (!ref) return base;
|
|
1213
|
-
return `${base}${
|
|
971
|
+
return `${base}${consoleProjectPath(ref, page)}`;
|
|
1214
972
|
}
|
|
1215
973
|
function userlandProjectUrl(ref, page = "", apiHint) {
|
|
1216
974
|
if (!ref) return "";
|
|
1217
975
|
try {
|
|
1218
|
-
return
|
|
976
|
+
return userlandUrl(ref, page, {
|
|
1219
977
|
base: mcpOrigins(apiHint).userland
|
|
1220
978
|
});
|
|
1221
979
|
} catch {
|
|
@@ -1583,7 +1341,7 @@ async function create_handler(args) {
|
|
|
1583
1341
|
const resolvedParent = args.parentDir ? node_path.resolve(args.parentDir) : process.cwd();
|
|
1584
1342
|
const gitInit = node_fs.existsSync(node_path.join(result.projectPath, ".git"));
|
|
1585
1343
|
const wwwOrigin = mcpOrigins().www;
|
|
1586
|
-
const deployUrl = `${wwwOrigin}${
|
|
1344
|
+
const deployUrl = `${wwwOrigin}${wwwNewPath({
|
|
1587
1345
|
template: result.template
|
|
1588
1346
|
})}`;
|
|
1589
1347
|
const catalogUrl = templateCatalogUrl(result.template, "mcp-create");
|
|
@@ -1628,7 +1386,7 @@ async function create_handler(args) {
|
|
|
1628
1386
|
]
|
|
1629
1387
|
});
|
|
1630
1388
|
}
|
|
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"}]}');
|
|
1389
|
+
var templates_meta_snapshot_namespaceObject = JSON.parse('{"version":"2","sourceRepo":"extension-js/examples","generatorVersion":"2","commit":"8a565020924a0f8be3ae9fa1c807999ba1b938e4","generatedAt":"2026-08-05T08:25:06.981Z","templates":[{"slug":"action","name":"action","title":"Action (Popup)","version":"1.0.0","manifest_version":3,"description":"Adds a toolbar popup to the browser with a simple page.","uiContext":["action","background"],"surfaces":["action","background"],"entrypoints":["./action/index.html","background.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":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","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/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":"Adds a toolbar popup whose text follows your browser\'s language.","uiContext":["action","background"],"surfaces":["action","background"],"entrypoints":["./action/index.html","background.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,"docsUrl":"https://extension.js.org/docs/implementation-guide/locales","files":["_locales/en/messages.json","_locales/pt_BR/messages.json","extension.config.js","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/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":"Adds a sidebar panel where you can chat with ChatGPT about the page you are viewing.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel","storage","activeTab","scripting","tabs"],"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, and 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","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/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":"Adds a sidebar panel where you can chat with Claude about the page you are viewing.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel","storage","activeTab","scripting","tabs"],"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, and 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","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/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":"Adds a sidebar panel where you can chat with Gemini about the page you are viewing.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel","storage","activeTab","scripting","tabs"],"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, and 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","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/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":"Adds a sidebar panel where you can chat with Perplexity about the page you are viewing.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel","storage","activeTab","scripting","tabs"],"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, and 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","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/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":"Injects a small badge into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.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":false,"docsUrl":"https://extension.js.org/docs/implementation-guide/content-scripts","files":["extension.config.js","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/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":"Injects a small styled badge into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.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":false,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/css-modules","files":["extension.config.js","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/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":"Injects a badge rendered in a custom font into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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,"docsUrl":"https://extension.js.org/docs/implementation-guide/css","files":["extension.config.js","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/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":"Injects a small panel showing the extension\'s environment values into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.ts","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,"docsUrl":"https://extension.js.org/docs/features/environment-variables","files":["extension.config.js","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/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":"Injects a small styled badge into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/less","files":["extension.config.js","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/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":"Injects a small styled badge into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/less","files":["extension.config.js","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/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":"Injects a small UI into every web page from both the extension and the page\'s own JavaScript world.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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","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/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":"Injects four small elements into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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,"docsUrl":"https://extension.js.org/docs/implementation-guide/content-scripts","files":["extension.config.js","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/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":"Injects four small elements into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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 and 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","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/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":"Shows a small overlay UI on every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.ts","./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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/preact","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":"Shows a small overlay UI on every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.ts","./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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/react","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":"Injects a small styled badge into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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,"docsUrl":"https://extension.js.org/docs/implementation-guide/css","files":["extension.config.js","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/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":"Injects a small styled badge into every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.js","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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/sass","files":["extension.config.js","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/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":"Shows a small overlay UI on every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.ts","./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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/svelte","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":"Shows a small overlay UI on every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.ts","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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/typescript","files":["extension.config.js","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/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":"Shows a small overlay UI on every web page you visit.","uiContext":["content","background"],"surfaces":["content","background"],"entrypoints":["background.ts","./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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/vue","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 minimal extension that loads in your browser, ready to build on.","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,"docsUrl":"https://extension.js.org/docs/getting-started/immediately","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":"Adds a sidebar panel to the browser with a simple page.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":false,"docsUrl":"https://extension.js.org/docs/getting-started/templates/javascript","files":["extension.config.js","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":"New Tab","version":"1.0.0","manifest_version":3,"description":"Replaces your new tab page with a simple page you can customize.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.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,"docsUrl":"https://extension.js.org/docs/implementation-guide/html","files":["extension.config.js","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/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":"Replaces your new tab page with a launcher that opens the extension\'s sidebar panel.","uiContext":["content","newtab","sidebar","background"],"surfaces":["content","newtab","sidebar","background"],"entrypoints":["newtab/index.html","sidebar/index.html","background.js","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,"docsUrl":"https://extension.js.org/docs/browsers/browser-flags","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":"Replaces your new tab page with a simple page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/integrations/eslint","files":["extension.config.js","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/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":"Replaces your new tab page with a simple page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/integrations/prettier","files":["extension.config.js","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/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":"Replaces your new tab page with a simple page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/integrations/stylelint","files":["extension.config.js","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/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":"Replaces your new tab page with a page that hashes any text you type using SHA-256.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/node","files":["extension.config.js","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/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":"Replaces your new tab page with a page showing the extension\'s environment values.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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 and 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","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/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":"Replaces your new tab page with a simple styled page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/less","files":["extension.config.js","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/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":"Replaces your new tab page with a simple Preact page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/preact","files":["extension.config.js","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/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":"Replaces your new tab page with a simple React page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/react","files":["extension.config.js","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/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":"Replaces your new tab page with a small React app you can navigate across routes.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/react","files":["extension.config.js","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/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":"Replaces your new tab page with a simple styled page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/sass","files":["extension.config.js","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/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":"Replaces your new tab page with a simple Svelte page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/svelte","files":["extension.config.js","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/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":"Replaces your new tab page with a simple page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/typescript","files":["extension.config.js","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/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":"Replaces your new tab page with a simple Vue page.","uiContext":["newtab","background"],"surfaces":["newtab","background"],"entrypoints":["newtab/index.html","background.js"],"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,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/vue","files":["extension.config.js","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/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":"Adds a sidebar panel to the browser, covered end to end by Playwright tests.","uiContext":["sidebar","action","background"],"surfaces":["action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts"],"uiFramework":"","css":"css","configFiles":["tsconfig.json","extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{"test":"playwright test"},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["sidePanel"],"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","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/screenshot.png","icon":"examples/playwright/src/images/icon.png"},{"slug":"preact","name":"preact","title":"Preact","version":"1.0.0","manifest_version":3,"description":"Adds a sidebar panel to the browser with a simple Preact page.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/getting-started/templates/preact","files":["extension.config.js","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/screenshot.png","icon":"examples/preact/src/images/icon.png"},{"slug":"react","name":"react","title":"React","version":"1.0.0","manifest_version":3,"description":"Adds a sidebar panel to the browser with a simple React page.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/getting-started/templates/react","files":["extension.config.js","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/screenshot.png","icon":"examples/react/src/images/icon.png"},{"slug":"sidebar","name":"sidebar","title":"Sidebar page","version":"1.0.0","manifest_version":3,"description":"Adds a sidebar panel to the browser with a simple page.","uiContext":["sidebar","action","background"],"surfaces":["action","sidebar","background"],"entrypoints":["sidebar/index.html","background.js"],"uiFramework":"","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/implementation-guide/html","files":["extension.config.js","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/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":"Adds a sidebar panel to the browser showing Ant Design components.","uiContext":["sidebar","action","background"],"surfaces":["action","sidebar","background"],"entrypoints":["sidebar/index.html","background.js"],"uiFramework":"react","css":"css","configFiles":["extension.config.js"],"hasBackground":true,"hasEnv":false,"setup":{"packageManager":"pnpm","scripts":{},"env":{"hasEnvExample":false,"hasEnv":false}},"permissions":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":false,"docsUrl":"https://ant.design/docs/react/introduce","files":["extension.config.js","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/screenshot.png","icon":"examples/sidebar-antd/src/images/icon.png"},{"slug":"sidebar-monorepo-turborepo","name":"sidebar-monorepo-turborepo","title":"Sidebar Turborepo","version":"1.0.0","manifest_version":3,"description":"Adds a sidebar panel and shows a small overlay UI on every web page you visit.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.js","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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://turborepo.dev/docs","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"],"browsers":["chrome","edge","firefox"],"screenshot":"examples/sidebar-monorepo-turborepo/screenshot.png","icon":null},{"slug":"sidebar-shadcn","name":"sidebar-shadcn","title":"Sidebar shadcn","version":"1.0.0","manifest_version":3,"description":"Adds a sidebar panel to the browser built with shadcn/ui components.","uiContext":["sidebar","action","background"],"surfaces":["action","sidebar","background"],"entrypoints":["sidebar/index.html","background.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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/integrations/shadcnui","files":["extension.config.js","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/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 in a new tab when the extension loads.","uiContext":["background"],"surfaces":["background"],"entrypoints":["background.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":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/ and 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","pages/custom.html","pages/main.html","pages/main.js","pages/welcome.html","public/css/file.css","public/html/file.html","public/js/file.js","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/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":"Shows a small overlay on web pages and a toolbar popup that runs the extension\'s bundled scripts.","uiContext":["content","action","background"],"surfaces":["content","action","background"],"entrypoints":["background.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":false,"docsUrl":"https://extension.js.org/docs/features/special-folders","files":["extension.config.js","scripts/script-one.js","scripts/script-three.js","scripts/script-two.js","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/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":"Adds a sidebar panel to the browser with a simple Svelte page.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/getting-started/templates/svelte","files":["extension.config.js","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/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":"Adds a sidebar panel that classifies the active page or your selected text with on-device AI models.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.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":["sidePanel","storage","unlimitedStorage","activeTab","scripting","tabs","contextMenus"],"host_permissions":["<all_urls>"],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/languages-and-frameworks/webassembly","files":["extension.config.js","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/screenshot.png","icon":"examples/transformers-js/src/images/icon.png"},{"slug":"typescript","name":"typescript","title":"TypeScript","version":"1.0.0","manifest_version":3,"description":"Adds a sidebar panel to the browser with a simple page.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/getting-started/templates/typescript","files":["extension.config.js","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":"Adds a sidebar panel to the browser with a simple Vue page.","uiContext":["content","sidebar","action","background"],"surfaces":["content","action","sidebar","background"],"entrypoints":["sidebar/index.html","background.ts","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":["sidePanel"],"host_permissions":[],"optional_permissions":[],"featured":true,"docsUrl":"https://extension.js.org/docs/getting-started/templates/vue","files":["extension.config.js","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/screenshot.png","icon":"examples/vue/src/images/icon.png"}]}');
|
|
1632
1390
|
const CACHE_DIR = node_path.join(node_os.homedir(), ".cache", "extension-js");
|
|
1633
1391
|
const CACHE_FILE = node_path.join(CACHE_DIR, "templates-meta.json");
|
|
1634
1392
|
const CACHE_TTL_MS = 3600000;
|
|
@@ -6855,7 +6613,7 @@ async function preview_web_handler(args) {
|
|
|
6855
6613
|
const LOGIN_HINT = "Run extension_auth (action: login), or set EXTENSION_DEV_TOKEN (create one in the extension.dev dashboard).";
|
|
6856
6614
|
const shares_schema = {
|
|
6857
6615
|
name: "extension_shares",
|
|
6858
|
-
description: "List and revoke the public preview links this token has shared, which is what extension_preview_web share:true hands out. Pass action:'list' (the default) for every artifact the logged-in project owns, with its artifactId, name, version, live or dead state, createdAt, expiresAt, revokedAt, size, previewUrl, zipUrl and revokeUrl, so a link whose response you lost is findable again. Each row carries owner and sharedBy as the platform returned them. Read attribution.ownership for who may revoke a share: 'project' means the workspace holds it and any member can pull it back, 'personal' means one person holds it alone, 'unknown' means no owner was disclosed. Read attribution.credit as credit only, never access; it names the publisher, and reads 'CLI token <id>' or 'not recorded' when no person can be named. Pass action:'revoke' with an artifactId, or with any URL of the share, to kill one permanently. Pass projectPath to reconcile against the project's own append-only .extension.dev/shared-previews.json, which is read and never rewritten: a share made on another machine shows as remoteOnly, a record with no live artifact as localOnly. This needs the same token as sharing (extension_auth or EXTENSION_DEV_TOKEN); without one, listing still returns the local record with a login hint.",
|
|
6616
|
+
description: "List and revoke the public preview links this token has shared, which is what extension_preview_web share:true hands out. Pass action:'list' (the default) for every artifact the logged-in project owns, with its artifactId, name, version, live or dead state, createdAt, expiresAt, revokedAt, size, previewUrl, zipUrl and revokeUrl, so a link whose response you lost is findable again. Each row carries owner and sharedBy as the platform returned them. Read attribution.ownership for who may revoke a share: 'project' means the workspace holds it and any member can pull it back, 'personal' means one person holds it alone, 'unknown' means no owner was disclosed. Read attribution.credit as credit only, never access; it names the publisher, and reads 'CLI token <id>' or 'not recorded' when no person can be named. Pass action:'revoke' with an artifactId, or with any URL of the share, to kill one permanently. Pass projectPath to reconcile against the project's own append-only .extension.dev/shared-previews.json, which is read and never rewritten: a share made on another machine shows as remoteOnly, a record with no live artifact as localOnly. That record is append-only, so localOnly is counted by distinct artifactId and a build re-shared unchanged is one share, not two; server.count and server.matched are share counts, while server.scanned counts records the platform read and is never a share count. This needs the same token as sharing (extension_auth or EXTENSION_DEV_TOKEN); without one, listing still returns the local record with a login hint.",
|
|
6859
6617
|
inputSchema: {
|
|
6860
6618
|
type: "object",
|
|
6861
6619
|
properties: {
|
|
@@ -6952,6 +6710,11 @@ function localIndex(entries) {
|
|
|
6952
6710
|
}
|
|
6953
6711
|
return index;
|
|
6954
6712
|
}
|
|
6713
|
+
function localShares(entries) {
|
|
6714
|
+
return [
|
|
6715
|
+
...localIndex(entries).values()
|
|
6716
|
+
];
|
|
6717
|
+
}
|
|
6955
6718
|
function localOnlyStatus(entry, completeness, liveFiltered, now) {
|
|
6956
6719
|
if ("cut" === completeness) return "unknown: the platform list was cut short, so this share may simply be past the returned window.";
|
|
6957
6720
|
if ("unsaid" === completeness) return "unknown: the platform did not say whether that list was whole, so this share may simply be past the returned window.";
|
|
@@ -6967,6 +6730,8 @@ async function listShares(args) {
|
|
|
6967
6730
|
path: local.path,
|
|
6968
6731
|
exists: local.exists,
|
|
6969
6732
|
entries: local.entries.length,
|
|
6733
|
+
shares: localIndex(local.entries).size,
|
|
6734
|
+
countsNote: "entries is rows in the append-only file and shares is distinct artifact ids in it. They differ when a build was re-shared unchanged, which appends a second row for the same link. shares is the number of links this project ever minted.",
|
|
6970
6735
|
...local.unreadable ? {
|
|
6971
6736
|
unreadable: "The file exists but could not be parsed, so no local reconciliation was possible."
|
|
6972
6737
|
} : {}
|
|
@@ -7000,7 +6765,7 @@ async function listShares(args) {
|
|
|
7000
6765
|
} : {}
|
|
7001
6766
|
},
|
|
7002
6767
|
shares: [],
|
|
7003
|
-
localOnly: (local?.entries ?? []).map((entry)=>({
|
|
6768
|
+
localOnly: localShares(local?.entries ?? []).map((entry)=>({
|
|
7004
6769
|
...entry,
|
|
7005
6770
|
...entry.revokeUrl ? {
|
|
7006
6771
|
revokeUrl: wwwRevokeUrl(entry.revokeUrl)
|
|
@@ -7039,7 +6804,9 @@ async function listShares(args) {
|
|
|
7039
6804
|
};
|
|
7040
6805
|
});
|
|
7041
6806
|
const completeness = listing.data.truncatedReported ? listing.data.truncated ? "cut" : "whole" : "unsaid";
|
|
7042
|
-
const localOnly =
|
|
6807
|
+
const localOnly = [
|
|
6808
|
+
...byId.values()
|
|
6809
|
+
].filter((entry)=>!seen.has(entry.artifactId)).map((entry)=>({
|
|
7043
6810
|
...entry,
|
|
7044
6811
|
...entry.revokeUrl ? {
|
|
7045
6812
|
revokeUrl: wwwRevokeUrl(entry.revokeUrl)
|
|
@@ -7068,6 +6835,7 @@ async function listShares(args) {
|
|
|
7068
6835
|
truncated: listing.data.truncated,
|
|
7069
6836
|
truncatedReported: listing.data.truncatedReported,
|
|
7070
6837
|
scanned: listing.data.scanned,
|
|
6838
|
+
scannedNote: "count and matched are shares: count is the shares on this page, matched is how many shares the filter found, and matched is a floor when truncated is true. scanned is not a share count and must not be read as one: it is how many stored records the platform opened to answer, including records that turned out to belong to someone else or to be filtered out. It tracks the platform's work, so it can sit above or beside these numbers by coincidence. ownership breaks the shares on this page down by who may revoke them and sums to the number of rows in shares.",
|
|
7071
6839
|
ownership,
|
|
7072
6840
|
...truncatedNote ? {
|
|
7073
6841
|
truncatedNote
|
|
@@ -7079,7 +6847,7 @@ async function listShares(args) {
|
|
|
7079
6847
|
} : {},
|
|
7080
6848
|
localRecord
|
|
7081
6849
|
},
|
|
7082
|
-
hint: `${liveCount} of ${shares.length} listed shares still resolve${local ? `; ${localOnly.length}
|
|
6850
|
+
hint: `${liveCount} of ${shares.length} listed shares still resolve${local ? `; ${localOnly.length} recorded ${1 === localOnly.length ? "share is" : "shares are"} ${liveFiltered ? 'not in this live-only listing (possibly dead rather than not owned; rerun with status:"all" to tell)' : "without an artifact behind them"}` : ""}. Revoke one with action:"revoke" and its artifactId or any of its URLs.`,
|
|
7083
6851
|
warnings: [
|
|
7084
6852
|
"previewUrl and zipUrl are null for a share that is no longer live, because a revoked or expired link cannot resolve for anyone. revokeUrl stays on every row. Revocation is permanent: a revoked id is burned, and re-sharing that build mints a different link. Re-sharing a build that was NOT revoked returns its existing link instead.",
|
|
7085
6853
|
"attribution.ownership says who the share belongs to and therefore who may revoke it: project means the owning workspace holds it and any member can pull it back, personal means one person holds it alone. attribution.credit names the publisher and is attribution only, granting and restricting nothing. A credit of \"CLI token ...\" means the platform could not resolve which human minted that token, and a credit of \"not recorded\" means it never knew; neither is a name, and neither should be reported as one.",
|
|
@@ -10943,8 +10711,8 @@ async function release_promote_handler(args) {
|
|
|
10943
10711
|
});
|
|
10944
10712
|
}
|
|
10945
10713
|
const promotedRef = resolveProjectRef();
|
|
10946
|
-
const publicChannelUrl = userlandProjectUrl(promotedRef,
|
|
10947
|
-
const publicBuildUrl = userlandProjectUrl(promotedRef,
|
|
10714
|
+
const publicChannelUrl = userlandProjectUrl(promotedRef, UserlandProjectPage.channel(channel), args.api);
|
|
10715
|
+
const publicBuildUrl = userlandProjectUrl(promotedRef, UserlandProjectPage.build(buildId), args.api);
|
|
10948
10716
|
const enriched = data && "object" == typeof data && !Array.isArray(data) ? {
|
|
10949
10717
|
...data,
|
|
10950
10718
|
...publicChannelUrl ? {
|
|
@@ -11010,11 +10778,11 @@ async function readReleases(args) {
|
|
|
11010
10778
|
const publicProjectUrl = userlandProjectUrl(ref, "", args.api);
|
|
11011
10779
|
const channelsWithUrls = channels.map((c)=>({
|
|
11012
10780
|
...c,
|
|
11013
|
-
publicUrl: userlandProjectUrl(ref,
|
|
10781
|
+
publicUrl: userlandProjectUrl(ref, UserlandProjectPage.channel(c.channel), args.api)
|
|
11014
10782
|
}));
|
|
11015
10783
|
const buildsWithUrls = recentBuilds.map((b)=>({
|
|
11016
10784
|
...b,
|
|
11017
|
-
publicUrl: userlandProjectUrl(ref,
|
|
10785
|
+
publicUrl: userlandProjectUrl(ref, UserlandProjectPage.build(b.sha), args.api)
|
|
11018
10786
|
}));
|
|
11019
10787
|
const publicUrlNote = publicProjectUrl ? isPrivate ? "publicUrl links open only for workspace members. This project is private, so an outside recipient needs a share link from extension_publish." : "publicUrl links are the public build pages: no login needed, and they carry the per-browser downloads and the run locally instructions." : null;
|
|
11020
10788
|
const channelsUnavailable = channelsRes.ok ? null : `channels.json unreadable: ${channelsRes.message}`;
|
|
@@ -12061,9 +11829,9 @@ const FEATURE_TEMPLATE_MAP = {
|
|
|
12061
11829
|
sidebar: {
|
|
12062
11830
|
react: "sidebar-shadcn",
|
|
12063
11831
|
vanilla: "sidebar",
|
|
12064
|
-
vue: "
|
|
12065
|
-
svelte: "
|
|
12066
|
-
preact: "
|
|
11832
|
+
vue: "vue",
|
|
11833
|
+
svelte: "svelte",
|
|
11834
|
+
preact: "preact"
|
|
12067
11835
|
},
|
|
12068
11836
|
"content-script": {
|
|
12069
11837
|
react: "content-react",
|
|
@@ -12092,20 +11860,6 @@ const FEATURE_TEMPLATE_MAP = {
|
|
|
12092
11860
|
vue: "javascript",
|
|
12093
11861
|
svelte: "javascript",
|
|
12094
11862
|
preact: "javascript"
|
|
12095
|
-
},
|
|
12096
|
-
options: {
|
|
12097
|
-
react: "javascript",
|
|
12098
|
-
vanilla: "javascript",
|
|
12099
|
-
vue: "javascript",
|
|
12100
|
-
svelte: "javascript",
|
|
12101
|
-
preact: "javascript"
|
|
12102
|
-
},
|
|
12103
|
-
devtools: {
|
|
12104
|
-
react: "javascript",
|
|
12105
|
-
vanilla: "javascript",
|
|
12106
|
-
vue: "javascript",
|
|
12107
|
-
svelte: "javascript",
|
|
12108
|
-
preact: "javascript"
|
|
12109
11863
|
}
|
|
12110
11864
|
};
|
|
12111
11865
|
const MANIFEST_ADDITIONS = {
|
|
@@ -12184,16 +11938,7 @@ async function add_feature_handler(args) {
|
|
|
12184
11938
|
hint: "Ensure projectPath points to an extension project root with src/manifest.json"
|
|
12185
11939
|
});
|
|
12186
11940
|
const templateSlug = FEATURE_TEMPLATE_MAP[args.feature]?.[framework];
|
|
12187
|
-
|
|
12188
|
-
ok: false,
|
|
12189
|
-
command: add_feature_COMMAND,
|
|
12190
|
-
status: "no-reference-template",
|
|
12191
|
-
error: {
|
|
12192
|
-
code: "E_NO_REFERENCE_TEMPLATE",
|
|
12193
|
-
message: `No reference template for feature "${args.feature}" with framework "${framework}"`
|
|
12194
|
-
}
|
|
12195
|
-
});
|
|
12196
|
-
const template = await getTemplateBySlug(templateSlug);
|
|
11941
|
+
const template = templateSlug ? await getTemplateBySlug(templateSlug) : void 0;
|
|
12197
11942
|
const referenceFiles = template?.keyFiles ?? template?.files ?? [];
|
|
12198
11943
|
const FEATURE_DIR = {
|
|
12199
11944
|
"content-script": "content",
|
|
@@ -12274,12 +12019,14 @@ async function add_feature_handler(args) {
|
|
|
12274
12019
|
value: {
|
|
12275
12020
|
feature: args.feature,
|
|
12276
12021
|
framework,
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12022
|
+
...templateSlug ? {
|
|
12023
|
+
referenceTemplate: {
|
|
12024
|
+
slug: templateSlug,
|
|
12025
|
+
repositoryUrl: `${EXAMPLES_TREE_BASE}/${templateSlug}`,
|
|
12026
|
+
catalogUrl: templateCatalogUrl(templateSlug),
|
|
12027
|
+
referenceFiles: referenceFiles.filter((f)=>f.includes(featureDir) || f.includes("manifest"))
|
|
12028
|
+
}
|
|
12029
|
+
} : {},
|
|
12283
12030
|
manifestUpdates,
|
|
12284
12031
|
filesToCreate: filesToCreate.map((f)=>({
|
|
12285
12032
|
...f,
|
|
@@ -12291,7 +12038,7 @@ async function add_feature_handler(args) {
|
|
|
12291
12038
|
"2. Create the following files in your project:",
|
|
12292
12039
|
...filesToCreate.map((f)=>` - ${f.path} (${f.hint})`),
|
|
12293
12040
|
"sidebar" === args.feature ? "3. Add background.ts to handle sidebar open: chromium uses chrome.sidePanel.setPanelBehavior, firefox uses browser.sidebarAction.open()" : "",
|
|
12294
|
-
`4. Reference template source: ${EXAMPLES_TREE_BASE}/${templateSlug}/src
|
|
12041
|
+
templateSlug ? `4. Reference template source: ${EXAMPLES_TREE_BASE}/${templateSlug}/src` : "4. No catalog template ships this surface yet: build from the manifest additions and file hints above",
|
|
12295
12042
|
"5. Run npm run dev to test"
|
|
12296
12043
|
].filter(Boolean)
|
|
12297
12044
|
},
|