extension-create 4.1.19 → 4.1.21
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/dist/lib/package-manager.d.ts +1 -0
- package/dist/lib/project-name.d.ts +1 -0
- package/dist/module.cjs +10 -2
- package/dist/steps/import-external-template.d.ts +1 -1
- package/dist/steps/write-package-json.d.ts +0 -2
- package/dist/test-template-javascript/.extension-create.json +3 -3
- package/dist/test-template-javascript/STORE.md +1 -1
- package/dist/test-template-javascript/package.json +1 -1
- package/dist/test-template-javascript/src/background.ts +46 -7
- package/dist/test-template-javascript/src/content/ContentApp.ts +20 -11
- package/dist/test-template-javascript/src/content/scripts.ts +1 -0
- package/dist/test-template-javascript/src/content/styles.css +9 -3
- package/dist/test-template-javascript/src/manifest.json +2 -1
- package/dist/test-template-javascript/src/sidebar/styles.css +8 -5
- package/package.json +2 -2
- package/templates/javascript/src/background.js +46 -7
- package/templates/javascript/src/content/ContentApp.js +23 -17
- package/templates/javascript/src/content/scripts.js +1 -0
- package/templates/javascript/src/content/styles.css +9 -3
- package/templates/javascript/src/manifest.json +2 -1
- package/templates/javascript/src/sidebar/styles.css +8 -5
|
@@ -3,6 +3,7 @@ declare const NODE_PACKAGE_MANAGERS: readonly ['pnpm', 'yarn', 'bun', 'npm'];
|
|
|
3
3
|
type NodePackageManager = (typeof NODE_PACKAGE_MANAGERS)[number];
|
|
4
4
|
export declare function detectPackageManagerFromEnv(): NodePackageManager;
|
|
5
5
|
export declare function isDenoRuntime(): boolean;
|
|
6
|
+
export declare function isBunRuntime(): boolean;
|
|
6
7
|
export type ScaffoldPackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun' | 'deno';
|
|
7
8
|
export declare function resolveScaffoldPackageManager(): ScaffoldPackageManager;
|
|
8
9
|
export declare function resolveProjectPackageManager(projectPath: string): ScaffoldPackageManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isUrlProjectName(projectNameInput: string): boolean;
|
package/dist/module.cjs
CHANGED
|
@@ -136,8 +136,12 @@ function detectPackageManagerFromEnv() {
|
|
|
136
136
|
function isDenoRuntime() {
|
|
137
137
|
return void 0 !== globalThis.Deno || Boolean(process.versions?.deno);
|
|
138
138
|
}
|
|
139
|
+
function isBunRuntime() {
|
|
140
|
+
return void 0 !== globalThis.Bun || Boolean(process.versions?.bun);
|
|
141
|
+
}
|
|
139
142
|
function resolveScaffoldPackageManager() {
|
|
140
143
|
if (isDenoRuntime()) return 'deno';
|
|
144
|
+
if (isBunRuntime()) return 'bun';
|
|
141
145
|
return detectPackageManagerFromEnv();
|
|
142
146
|
}
|
|
143
147
|
function resolveProjectPackageManager(projectPath) {
|
|
@@ -145,6 +149,7 @@ function resolveProjectPackageManager(projectPath) {
|
|
|
145
149
|
const pinned = readPinnedPackageManager(projectPath);
|
|
146
150
|
if (pinned) return pinned;
|
|
147
151
|
if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(projectPath, 'pnpm-workspace.yaml'))) return 'pnpm';
|
|
152
|
+
if (isBunRuntime()) return 'bun';
|
|
148
153
|
return detectPackageManagerFromEnv();
|
|
149
154
|
}
|
|
150
155
|
function readPinnedPackageManager(projectPath) {
|
|
@@ -358,6 +363,9 @@ function keepingExistingGitignore(projectName) {
|
|
|
358
363
|
function existingRepositoryKept(projectName) {
|
|
359
364
|
return `${prefix('info')} ${external_pintor_default().blue(projectName)} is already a git repository with history.\nLeft it uncommitted. Review the scaffold and run ${external_pintor_default().blue('git add -A && git commit')} yourself.`;
|
|
360
365
|
}
|
|
366
|
+
function isUrlProjectName(projectNameInput) {
|
|
367
|
+
return /^https?:\/\//i.test(projectNameInput.trim());
|
|
368
|
+
}
|
|
361
369
|
const promises_namespaceObject = require("node:fs/promises");
|
|
362
370
|
async function copyDirectoryWithSymlinks(source, destination) {
|
|
363
371
|
const entries = await promises_namespaceObject.readdir(source, {
|
|
@@ -609,7 +617,7 @@ const NETWORK_TIMEOUT_MS = (()=>{
|
|
|
609
617
|
return Number.isFinite(raw) && raw > 0 ? raw : 60000;
|
|
610
618
|
})();
|
|
611
619
|
const CODELOAD_BASE = 'https://codeload.github.com/extension-js/examples/zip';
|
|
612
|
-
const DEFAULT_TEMPLATES_REF = '
|
|
620
|
+
const DEFAULT_TEMPLATES_REF = '06d33e35894516a460409dca06d1614e291e6252';
|
|
613
621
|
const BUNDLED_TEMPLATES = [
|
|
614
622
|
"javascript"
|
|
615
623
|
];
|
|
@@ -2135,7 +2143,7 @@ async function extensionCreate(projectNameInput, { cliVersion, template, install
|
|
|
2135
2143
|
if (!projectNameInput) throw new Error(noProjectName());
|
|
2136
2144
|
const templateWasOmitted = null == template || '' === String(template).trim();
|
|
2137
2145
|
const effectiveTemplate = templateWasOmitted ? DEFAULT_TEMPLATE_NAME : String(template);
|
|
2138
|
-
if (projectNameInput
|
|
2146
|
+
if (isUrlProjectName(projectNameInput)) throw new Error(noUrlAllowed());
|
|
2139
2147
|
const projectPath = external_node_path_namespaceObject.isAbsolute(projectNameInput) ? projectNameInput : external_node_path_namespaceObject.join(process.cwd(), projectNameInput);
|
|
2140
2148
|
const projectName = external_node_path_namespaceObject.basename(projectPath);
|
|
2141
2149
|
const updateSuffix = process.env.EXTENSION_CLI_UPDATE_SUFFIX || '';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_TEMPLATES_REF = "
|
|
1
|
+
export declare const DEFAULT_TEMPLATES_REF = "06d33e35894516a460409dca06d1614e291e6252";
|
|
2
2
|
export declare const BUNDLED_TEMPLATES: readonly string[];
|
|
3
3
|
export declare const DEFAULT_TEMPLATE_NAME = "typescript";
|
|
4
4
|
export declare const OFFLINE_FALLBACK_TEMPLATE = "javascript";
|
|
@@ -2,10 +2,8 @@ import { type ScaffoldPackageManager } from '../lib/package-manager.js';
|
|
|
2
2
|
export declare function resolveExtensionBinary(): Promise<string>;
|
|
3
3
|
export declare function getTemplateAwareScripts(template: string, extensionBinary: string): Record<string, string>;
|
|
4
4
|
interface OverridePackageJsonOptions {
|
|
5
|
-
/** Defaults to `javascript` when omitted (same as `extensionCreate`). */
|
|
6
5
|
template?: string;
|
|
7
6
|
cliVersion?: string;
|
|
8
|
-
/** The one manager the project uses; resolved from the scaffold when omitted. */
|
|
9
7
|
packageManager?: ScaffoldPackageManager;
|
|
10
8
|
}
|
|
11
9
|
export declare function resolveExtensionDevDependencyVersion(cliVersion?: string): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"createdWith": "extension-create@4.1.
|
|
2
|
+
"createdWith": "extension-create@4.1.20",
|
|
3
3
|
"template": "typescript",
|
|
4
|
-
"source": "https://codeload.github.com/extension-js/examples/zip/
|
|
5
|
-
"ref": "
|
|
4
|
+
"source": "https://codeload.github.com/extension-js/examples/zip/06d33e35894516a460409dca06d1614e291e6252",
|
|
5
|
+
"ref": "06d33e35894516a460409dca06d1614e291e6252"
|
|
6
6
|
}
|
|
@@ -9,7 +9,7 @@ Packaging your extension is local and free. Submitting the result to a
|
|
|
9
9
|
store is what [extension.dev](https://docs.extension.dev/publish/overview?utm_source=store-md)
|
|
10
10
|
does, and it sponsors Extension.js.
|
|
11
11
|
|
|
12
|
-
Last updated: 2026-09-
|
|
12
|
+
Last updated: 2026-09-17
|
|
13
13
|
|
|
14
14
|
## Listing
|
|
15
15
|
|
|
@@ -6,22 +6,61 @@ const isFirefoxLike =
|
|
|
6
6
|
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'firefox' ||
|
|
7
7
|
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'gecko-based'
|
|
8
8
|
|
|
9
|
+
const isSafariLike =
|
|
10
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'safari' ||
|
|
11
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'webkit-based'
|
|
12
|
+
|
|
13
|
+
// Safari has no side panel surface, so the sidebar page opens in a tab.
|
|
14
|
+
let sidebarTabId: number | undefined
|
|
15
|
+
|
|
16
|
+
function openSidebarTab() {
|
|
17
|
+
const url = chrome.runtime.getURL('sidebar/index.html')
|
|
18
|
+
|
|
19
|
+
const openNewTab = () => {
|
|
20
|
+
chrome.tabs.create({url}, (tab) => {
|
|
21
|
+
sidebarTabId = tab?.id
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A repeat click focuses the tab already opened instead of a new copy.
|
|
26
|
+
const knownTabId = sidebarTabId
|
|
27
|
+
|
|
28
|
+
if (knownTabId === undefined) {
|
|
29
|
+
openNewTab()
|
|
30
|
+
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
chrome.tabs.update(knownTabId, {active: true}, () => {
|
|
35
|
+
if (chrome.runtime.lastError) openNewTab()
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
9
39
|
if (isFirefoxLike) {
|
|
40
|
+
// Firefox refuses sidebarAction.open() outside a user input handler, and a
|
|
41
|
+
// message listener is not one, so the toolbar click is the only route.
|
|
10
42
|
browser.browserAction.onClicked.addListener(() => {
|
|
11
43
|
browser.sidebarAction.open()
|
|
12
44
|
})
|
|
45
|
+
}
|
|
13
46
|
|
|
14
|
-
|
|
47
|
+
if (isSafariLike) {
|
|
48
|
+
// Safari never had setPanelBehavior, so the toolbar click needs a listener.
|
|
49
|
+
chrome.action?.onClicked.addListener(() => {
|
|
50
|
+
openSidebarTab()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
chrome.runtime.onMessage.addListener((message) => {
|
|
15
54
|
if (!message || message.type !== 'openSidebar') return
|
|
16
55
|
|
|
17
|
-
|
|
56
|
+
openSidebarTab()
|
|
18
57
|
})
|
|
19
58
|
}
|
|
20
59
|
|
|
21
|
-
if (!isFirefoxLike) {
|
|
60
|
+
if (!isFirefoxLike && !isSafariLike) {
|
|
22
61
|
// setPanelBehavior only affects FUTURE action clicks, registering it
|
|
23
62
|
// inside onClicked would swallow the first toolbar click.
|
|
24
|
-
chrome.sidePanel
|
|
63
|
+
chrome.sidePanel?.setPanelBehavior({openPanelOnActionClick: true})
|
|
25
64
|
|
|
26
65
|
// The side panel API only exists in Chromium. Firefox opens the sidebar in
|
|
27
66
|
// the listener above, so this listener is compiled out of gecko builds.
|
|
@@ -32,13 +71,13 @@ if (!isFirefoxLike) {
|
|
|
32
71
|
// allowed inside the user gesture that the content-script click carries, and
|
|
33
72
|
// a tabs.query callback outlives it: the panel then silently refuses to open.
|
|
34
73
|
// sender.tab is the tab the click came from, so no lookup is needed at all.
|
|
35
|
-
chrome.sidePanel
|
|
74
|
+
chrome.sidePanel?.setPanelBehavior({openPanelOnActionClick: true})
|
|
36
75
|
|
|
37
76
|
const tabId = sender.tab?.id
|
|
38
|
-
if (!chrome.sidePanel
|
|
77
|
+
if (!chrome.sidePanel?.open || tabId === undefined) return
|
|
39
78
|
|
|
40
79
|
try {
|
|
41
|
-
chrome.sidePanel
|
|
80
|
+
chrome.sidePanel?.open({tabId})
|
|
42
81
|
} catch (error) {
|
|
43
82
|
console.error(error)
|
|
44
83
|
}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import logo from '../images/icon.png'
|
|
2
2
|
|
|
3
|
+
const isFirefoxLike =
|
|
4
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'firefox' ||
|
|
5
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'gecko-based'
|
|
6
|
+
|
|
3
7
|
export default function createContentApp(): HTMLDivElement {
|
|
4
8
|
const container = document.createElement('div')
|
|
5
9
|
container.className = 'content_script'
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
pill
|
|
10
|
-
pill.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// Firefox cannot open a sidebar from a message listener, so the gecko build
|
|
12
|
+
// renders a hint naming the toolbar action instead of a dead control.
|
|
13
|
+
const pill = document.createElement(isFirefoxLike ? 'div' : 'button')
|
|
14
|
+
pill.className = isFirefoxLike
|
|
15
|
+
? 'content_pill content_pill_static'
|
|
16
|
+
: 'content_pill'
|
|
17
|
+
|
|
18
|
+
if (!isFirefoxLike) {
|
|
19
|
+
;(pill as HTMLButtonElement).type = 'button'
|
|
20
|
+
pill.setAttribute('aria-label', 'Open sidebar')
|
|
21
|
+
pill.addEventListener('click', () => {
|
|
15
22
|
chrome.runtime.sendMessage({type: 'openSidebar'})
|
|
16
|
-
}
|
|
17
|
-
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
18
25
|
|
|
19
26
|
const img = document.createElement('img')
|
|
20
27
|
img.className = 'content_pill_logo'
|
|
@@ -26,7 +33,9 @@ export default function createContentApp(): HTMLDivElement {
|
|
|
26
33
|
|
|
27
34
|
const text = document.createElement('span')
|
|
28
35
|
text.className = 'content_pill_text'
|
|
29
|
-
text.textContent =
|
|
36
|
+
text.textContent = isFirefoxLike
|
|
37
|
+
? 'Use the toolbar icon to open the sidebar'
|
|
38
|
+
: 'Open sidebar'
|
|
30
39
|
|
|
31
40
|
pill.appendChild(img)
|
|
32
41
|
pill.appendChild(text)
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
.content_pill:hover {
|
|
25
|
+
.content_pill:not(.content_pill_static):hover {
|
|
26
26
|
background: #11151c;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -40,6 +40,12 @@
|
|
|
40
40
|
font-weight: 600;
|
|
41
41
|
line-height: 1;
|
|
42
42
|
font-family:
|
|
43
|
-
-apple-system, BlinkMacSystemFont,
|
|
44
|
-
Arial,
|
|
43
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
44
|
+
Arial, 'Noto Sans', sans-serif;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* The gecko build renders this hint in place of the pill, so it must not read
|
|
48
|
+
as a control: no pointer cursor and no hover response. */
|
|
49
|
+
.content_pill_static {
|
|
50
|
+
cursor: default;
|
|
45
51
|
}
|
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
body {
|
|
12
12
|
background-color: var(--sidebar-bg);
|
|
13
13
|
color: var(--sidebar-text);
|
|
14
|
-
|
|
14
|
+
font-family:
|
|
15
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
16
|
+
Arial, 'Noto Sans', sans-serif;
|
|
17
|
+
/* The margin insets the panel, so a full-viewport height would push the
|
|
18
|
+
content below centre and overflow the panel by twice the margin. */
|
|
19
|
+
height: calc(100vh - 2 * var(--sidebar-margin));
|
|
15
20
|
margin: var(--sidebar-margin);
|
|
16
21
|
border-radius: 6px;
|
|
17
22
|
display: flex;
|
|
@@ -26,11 +31,12 @@ body {
|
|
|
26
31
|
align-items: center;
|
|
27
32
|
padding: 0 1rem;
|
|
28
33
|
text-align: center;
|
|
29
|
-
max-height:
|
|
34
|
+
max-height: 100%;
|
|
30
35
|
overflow-y: auto;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
.sidebar_logo {
|
|
39
|
+
display: block;
|
|
34
40
|
width: 72px;
|
|
35
41
|
margin: 0 auto 1rem;
|
|
36
42
|
}
|
|
@@ -38,9 +44,6 @@ body {
|
|
|
38
44
|
.sidebar_title {
|
|
39
45
|
font-size: 1.85em;
|
|
40
46
|
line-height: 1.1;
|
|
41
|
-
font-family:
|
|
42
|
-
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
|
43
|
-
Arial, "Noto Sans", sans-serif;
|
|
44
47
|
font-weight: 700;
|
|
45
48
|
margin: 0;
|
|
46
49
|
text-align: center;
|
package/package.json
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"templates"
|
|
26
26
|
],
|
|
27
27
|
"name": "extension-create",
|
|
28
|
-
"version": "4.1.
|
|
28
|
+
"version": "4.1.21",
|
|
29
29
|
"description": "The standalone extension creation engine for Extension.js",
|
|
30
30
|
"author": {
|
|
31
31
|
"name": "Cezar Augusto",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"watch": "rslib build --watch",
|
|
46
46
|
"compile": "rslib build",
|
|
47
47
|
"format": "biome format --write .",
|
|
48
|
-
"lint": "biome lint .",
|
|
48
|
+
"lint": "biome lint . && eslint .",
|
|
49
49
|
"pretest:create": "pnpm compile",
|
|
50
50
|
"pretest": "pnpm compile",
|
|
51
51
|
"test": "vitest run",
|
|
@@ -6,22 +6,61 @@ const isFirefoxLike =
|
|
|
6
6
|
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'firefox' ||
|
|
7
7
|
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'gecko-based'
|
|
8
8
|
|
|
9
|
+
const isSafariLike =
|
|
10
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'safari' ||
|
|
11
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'webkit-based'
|
|
12
|
+
|
|
13
|
+
// Safari has no side panel surface, so the sidebar page opens in a tab.
|
|
14
|
+
let sidebarTabId
|
|
15
|
+
|
|
16
|
+
function openSidebarTab() {
|
|
17
|
+
const url = chrome.runtime.getURL('sidebar/index.html')
|
|
18
|
+
|
|
19
|
+
const openNewTab = () => {
|
|
20
|
+
chrome.tabs.create({url}, (tab) => {
|
|
21
|
+
sidebarTabId = tab?.id
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A repeat click focuses the tab already opened instead of a new copy.
|
|
26
|
+
const knownTabId = sidebarTabId
|
|
27
|
+
|
|
28
|
+
if (knownTabId === undefined) {
|
|
29
|
+
openNewTab()
|
|
30
|
+
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
chrome.tabs.update(knownTabId, {active: true}, () => {
|
|
35
|
+
if (chrome.runtime.lastError) openNewTab()
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
9
39
|
if (isFirefoxLike) {
|
|
40
|
+
// Firefox refuses sidebarAction.open() outside a user input handler, and a
|
|
41
|
+
// message listener is not one, so the toolbar click is the only route.
|
|
10
42
|
browser.browserAction.onClicked.addListener(() => {
|
|
11
43
|
browser.sidebarAction.open()
|
|
12
44
|
})
|
|
45
|
+
}
|
|
13
46
|
|
|
14
|
-
|
|
47
|
+
if (isSafariLike) {
|
|
48
|
+
// Safari never had setPanelBehavior, so the toolbar click needs a listener.
|
|
49
|
+
chrome.action?.onClicked.addListener(() => {
|
|
50
|
+
openSidebarTab()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
chrome.runtime.onMessage.addListener((message) => {
|
|
15
54
|
if (!message || message.type !== 'openSidebar') return
|
|
16
55
|
|
|
17
|
-
|
|
56
|
+
openSidebarTab()
|
|
18
57
|
})
|
|
19
58
|
}
|
|
20
59
|
|
|
21
|
-
if (!isFirefoxLike) {
|
|
60
|
+
if (!isFirefoxLike && !isSafariLike) {
|
|
22
61
|
// setPanelBehavior only affects FUTURE action clicks, registering it
|
|
23
62
|
// inside onClicked would swallow the first toolbar click.
|
|
24
|
-
chrome.sidePanel
|
|
63
|
+
chrome.sidePanel?.setPanelBehavior({openPanelOnActionClick: true})
|
|
25
64
|
|
|
26
65
|
// The side panel API only exists in Chromium. Firefox opens the sidebar in
|
|
27
66
|
// the listener above, so this listener is compiled out of gecko builds.
|
|
@@ -32,13 +71,13 @@ if (!isFirefoxLike) {
|
|
|
32
71
|
// allowed inside the user gesture that the content-script click carries, and
|
|
33
72
|
// a tabs.query callback outlives it: the panel then silently refuses to open.
|
|
34
73
|
// sender.tab is the tab the click came from, so no lookup is needed at all.
|
|
35
|
-
chrome.sidePanel
|
|
74
|
+
chrome.sidePanel?.setPanelBehavior({openPanelOnActionClick: true})
|
|
36
75
|
|
|
37
76
|
const tabId = sender.tab?.id
|
|
38
|
-
if (!chrome.sidePanel
|
|
77
|
+
if (!chrome.sidePanel?.open || tabId === undefined) return
|
|
39
78
|
|
|
40
79
|
try {
|
|
41
|
-
chrome.sidePanel
|
|
80
|
+
chrome.sidePanel?.open({tabId})
|
|
42
81
|
} catch (error) {
|
|
43
82
|
console.error(error)
|
|
44
83
|
}
|
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import logo from '../images/icon.png'
|
|
2
2
|
|
|
3
|
+
const isFirefoxLike =
|
|
4
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'firefox' ||
|
|
5
|
+
import.meta.env.EXTENSION_PUBLIC_BROWSER === 'gecko-based'
|
|
6
|
+
|
|
3
7
|
export default function createContentApp() {
|
|
4
8
|
const container = document.createElement('div')
|
|
5
9
|
container.className = 'content_script'
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
pill
|
|
10
|
-
pill.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
// Firefox cannot open a sidebar from a message listener, so the gecko build
|
|
12
|
+
// renders a hint naming the toolbar action instead of a dead control.
|
|
13
|
+
const pill = document.createElement(isFirefoxLike ? 'div' : 'button')
|
|
14
|
+
pill.className = isFirefoxLike
|
|
15
|
+
? 'content_pill content_pill_static'
|
|
16
|
+
: 'content_pill'
|
|
17
|
+
|
|
18
|
+
if (!isFirefoxLike) {
|
|
19
|
+
pill.type = 'button'
|
|
20
|
+
pill.setAttribute('aria-label', 'Open sidebar')
|
|
21
|
+
pill.addEventListener('click', () => {
|
|
22
|
+
try {
|
|
19
23
|
chrome.runtime.sendMessage({type: 'openSidebar'})
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error(error)
|
|
20
26
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
})
|
|
27
|
+
})
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
const img = document.createElement('img')
|
|
27
31
|
img.className = 'content_pill_logo'
|
|
@@ -31,7 +35,9 @@ export default function createContentApp() {
|
|
|
31
35
|
|
|
32
36
|
const text = document.createElement('span')
|
|
33
37
|
text.className = 'content_pill_text'
|
|
34
|
-
text.textContent =
|
|
38
|
+
text.textContent = isFirefoxLike
|
|
39
|
+
? 'Use the toolbar icon to open the sidebar'
|
|
40
|
+
: 'Open sidebar'
|
|
35
41
|
|
|
36
42
|
pill.appendChild(img)
|
|
37
43
|
pill.appendChild(text)
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
.content_pill:hover {
|
|
25
|
+
.content_pill:not(.content_pill_static):hover {
|
|
26
26
|
background: #11151c;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -40,6 +40,12 @@
|
|
|
40
40
|
font-weight: 600;
|
|
41
41
|
line-height: 1;
|
|
42
42
|
font-family:
|
|
43
|
-
-apple-system, BlinkMacSystemFont,
|
|
44
|
-
Arial,
|
|
43
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
44
|
+
Arial, 'Noto Sans', sans-serif;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* The gecko build renders this hint in place of the pill, so it must not read
|
|
48
|
+
as a control: no pointer cursor and no hover response. */
|
|
49
|
+
.content_pill_static {
|
|
50
|
+
cursor: default;
|
|
45
51
|
}
|
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
body {
|
|
12
12
|
background-color: var(--sidebar-bg);
|
|
13
13
|
color: var(--sidebar-text);
|
|
14
|
-
|
|
14
|
+
font-family:
|
|
15
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
16
|
+
Arial, 'Noto Sans', sans-serif;
|
|
17
|
+
/* The margin insets the panel, so a full-viewport height would push the
|
|
18
|
+
content below centre and overflow the panel by twice the margin. */
|
|
19
|
+
height: calc(100vh - 2 * var(--sidebar-margin));
|
|
15
20
|
margin: var(--sidebar-margin);
|
|
16
21
|
border-radius: 6px;
|
|
17
22
|
display: flex;
|
|
@@ -26,11 +31,12 @@ body {
|
|
|
26
31
|
align-items: center;
|
|
27
32
|
padding: 0 1rem;
|
|
28
33
|
text-align: center;
|
|
29
|
-
max-height:
|
|
34
|
+
max-height: 100%;
|
|
30
35
|
overflow-y: auto;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
.sidebar_logo {
|
|
39
|
+
display: block;
|
|
34
40
|
width: 72px;
|
|
35
41
|
margin: 0 auto 1rem;
|
|
36
42
|
}
|
|
@@ -38,9 +44,6 @@ body {
|
|
|
38
44
|
.sidebar_title {
|
|
39
45
|
font-size: 1.85em;
|
|
40
46
|
line-height: 1.1;
|
|
41
|
-
font-family:
|
|
42
|
-
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
|
43
|
-
Arial, "Noto Sans", sans-serif;
|
|
44
47
|
font-weight: 700;
|
|
45
48
|
margin: 0;
|
|
46
49
|
text-align: center;
|