extension-create 4.1.32 → 4.1.33

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/module.cjs CHANGED
@@ -783,7 +783,7 @@ const NETWORK_TIMEOUT_MS = (()=>{
783
783
  return Number.isFinite(raw) && raw > 0 ? raw : 60000;
784
784
  })();
785
785
  const CODELOAD_BASE = 'https://codeload.github.com/extension-js/examples/zip';
786
- const DEFAULT_TEMPLATES_REF = '2a8c4bae43ba6e883bf9ce7f6d108963ed162516';
786
+ const DEFAULT_TEMPLATES_REF = 'd69e46b311c27d11a98d241494023101b337281f';
787
787
  const BUNDLED_TEMPLATES = [
788
788
  "javascript"
789
789
  ];
@@ -1,4 +1,4 @@
1
- export declare const DEFAULT_TEMPLATES_REF = "2a8c4bae43ba6e883bf9ce7f6d108963ed162516";
1
+ export declare const DEFAULT_TEMPLATES_REF = "d69e46b311c27d11a98d241494023101b337281f";
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";
@@ -1,5 +1,5 @@
1
1
  {
2
- "createdWith": "extension-create@4.1.31",
2
+ "createdWith": "extension-create@4.1.32",
3
3
  "template": "typescript",
4
- "source": "http://127.0.0.1:41135/examples.zip"
4
+ "source": "http://127.0.0.1:36711/examples.zip"
5
5
  }
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "dependencies": {},
16
16
  "devDependencies": {
17
- "extension": "^4.1.31"
17
+ "extension": "^4.1.32"
18
18
  },
19
19
  "packageManager": "pnpm@10.28.0",
20
20
  "pnpm": {
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "templates"
26
26
  ],
27
27
  "name": "extension-create",
28
- "version": "4.1.32",
28
+ "version": "4.1.33",
29
29
  "description": "The standalone extension creation engine for Extension.js",
30
30
  "author": {
31
31
  "name": "Cezar Augusto",
@@ -43,6 +43,7 @@ src/
43
43
  │ └── icon.png
44
44
  ├── sidebar/
45
45
  │ ├── index.html
46
+ │ ├── page-title.js
46
47
  │ ├── scripts.js
47
48
  │ ├── SidebarApp.js
48
49
  │ └── styles.css
@@ -31,11 +31,22 @@ export default function initial() {
31
31
  const container = createContentApp()
32
32
  shadowRoot.appendChild(container)
33
33
 
34
+ // The sidebar asks this page for its title over runtime messaging, and the
35
+ // answer leaves with the UI on teardown so a reload never answers twice.
36
+ chrome.runtime.onMessage.addListener(answerPageTitle)
37
+
34
38
  return () => {
39
+ chrome.runtime.onMessage.removeListener(answerPageTitle)
35
40
  rootDiv.remove()
36
41
  }
37
42
  }
38
43
 
44
+ function answerPageTitle(message, _sender, sendResponse) {
45
+ if (!message || message.type !== 'getPageTitle') return
46
+
47
+ sendResponse({title: document.title, url: location.href})
48
+ }
49
+
39
50
  async function fetchCSS() {
40
51
  const cssUrl = new URL('./styles.css', import.meta.url)
41
52
  const response = await fetch(cssUrl)
@@ -49,7 +49,8 @@
49
49
  "default_title": "Side Panel Content",
50
50
  "browser_style": false
51
51
  },
52
- "chromium:permissions": ["sidePanel"],
52
+ "chromium:permissions": ["sidePanel", "tabs"],
53
+ "firefox:permissions": ["tabs"],
53
54
  "background": {
54
55
  "chromium:service_worker": "background.js",
55
56
  "firefox:scripts": ["background.js"]
@@ -1,5 +1,8 @@
1
1
  import './styles.css'
2
2
  import javascriptLogo from '../images/icon.png'
3
+ import {watchPageTitle} from './page-title.js'
4
+
5
+ const NO_PAGE_TEXT = 'Open a web page to see its title here.'
3
6
 
4
7
  function SidebarApp() {
5
8
  const root = document.getElementById('root')
@@ -27,8 +30,16 @@ function SidebarApp() {
27
30
  description.className = 'sidebar_description'
28
31
  description.append('Learn more in the ', link, ' .')
29
32
 
30
- app.append(logo, title, description)
33
+ const pageTitle = document.createElement('p')
34
+ pageTitle.className = 'sidebar_page_title'
35
+ pageTitle.textContent = NO_PAGE_TEXT
36
+
37
+ app.append(logo, title, description, pageTitle)
31
38
  root.replaceChildren(app)
39
+
40
+ watchPageTitle((answer) => {
41
+ pageTitle.textContent = answer ? answer.title : NO_PAGE_TEXT
42
+ })
32
43
  }
33
44
 
34
45
  SidebarApp()
@@ -0,0 +1,71 @@
1
+ // The panel's question to the page. The content script answers it with the
2
+ // page title and url, so the panel never reads the page on its own.
3
+ export const PAGE_TITLE_REQUEST = 'getPageTitle'
4
+
5
+ function isWebPage(tab) {
6
+ return typeof tab.url === 'string' && /^https?:/.test(tab.url)
7
+ }
8
+
9
+ // The page the panel is about. When the panel itself is open as a tab (Safari,
10
+ // or a test harness) the active tab is the panel, so the most recently used
11
+ // web tab stands in for it.
12
+ function findPageTab(callback) {
13
+ chrome.tabs.query({active: true, lastFocusedWindow: true}, (focused) => {
14
+ const current = focused?.[0]
15
+
16
+ if (current && isWebPage(current)) {
17
+ callback(current)
18
+
19
+ return
20
+ }
21
+
22
+ chrome.tabs.query({}, (tabs) => {
23
+ const webTabs = (tabs ?? []).filter(isWebPage)
24
+ webTabs.sort(
25
+ (a, b) =>
26
+ Number(b.active) - Number(a.active) ||
27
+ (b.lastAccessed ?? 0) - (a.lastAccessed ?? 0)
28
+ )
29
+
30
+ callback(webTabs[0])
31
+ })
32
+ })
33
+ }
34
+
35
+ function askPage(tab, onAnswer) {
36
+ chrome.tabs.sendMessage(tab.id, {type: PAGE_TITLE_REQUEST}, (answer) => {
37
+ // A page with no content script in it (a browser page, or one loaded
38
+ // before the extension was) has nobody to answer, so the hint stays up.
39
+ onAnswer(chrome.runtime.lastError ? undefined : answer)
40
+ })
41
+ }
42
+
43
+ // Keeps onChange fed with the current page's title, asking again whenever the
44
+ // active tab changes or a page finishes loading. Returns the function that
45
+ // stops watching.
46
+ export function watchPageTitle(onChange) {
47
+ const refresh = () => {
48
+ findPageTab((tab) => {
49
+ if (!tab) {
50
+ onChange(undefined)
51
+
52
+ return
53
+ }
54
+
55
+ askPage(tab, onChange)
56
+ })
57
+ }
58
+
59
+ const onUpdated = (_tabId, changeInfo) => {
60
+ if (changeInfo.status === 'complete' || changeInfo.title) refresh()
61
+ }
62
+
63
+ chrome.tabs.onActivated.addListener(refresh)
64
+ chrome.tabs.onUpdated.addListener(onUpdated)
65
+ refresh()
66
+
67
+ return () => {
68
+ chrome.tabs.onActivated.removeListener(refresh)
69
+ chrome.tabs.onUpdated.removeListener(onUpdated)
70
+ }
71
+ }
@@ -61,3 +61,12 @@ body {
61
61
  color: var(--sidebar-link);
62
62
  margin: 0;
63
63
  }
64
+
65
+ .sidebar_page_title {
66
+ font-size: small;
67
+ margin: 1rem 0 0;
68
+ padding-top: 0.75rem;
69
+ border-top: 1px solid var(--sidebar-border);
70
+ text-align: center;
71
+ word-break: break-word;
72
+ }