[New sample] Open Chrome API reference page (omnibox, alarms, messaging sample) (#848)

* First draft

* Rename folder

* Tweak comments

* Update index.html

* remove extra closing div

* Rename sw file

* Apply first round of @sebastianbenz suggestions

* Fix import

* Update popover

* Second tech review round
This commit is contained in:
amysteamdev
2024-08-21 05:53:13 -06:00
committed by GitHub
parent b3ff024b06
commit 90249bc956
9 changed files with 226 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
export default [
{
content: 'commands',
description:
'Use the <match>Commands API</match> to add a keyboard shortcuts.'
},
{
content: 'contextmenus',
description:
"Use the <match>ContextMenus API</match> to add a custom item to Chrome's context menu."
},
{
content: 'declarativeNetRequest',
description:
'Use the <match>DeclarativeNetRequest API</match> to block or modify network requests.'
},
{
content: 'downloads',
description:
'Use the <match>Downloads API</match> to programmatically manipulate downloads.'
},
{
content: 'i18n',
description: 'Use the <match>i18n API</match> to localize your extension'
},
{
content: 'identity',
description:
'Use the <match>Identity API</match> to get OAuth2 access tokens.'
},
{
content: 'notifications',
description:
'Use the <match>Notifications API</match> show notifications to users in the system tray.'
},
{
content: 'offscreen',
description:
'Use the <match>Offscreen API</match> to create and manage offscreen documents.'
},
{
content: 'omnibox',
description:
"Use the <match>Omnibox API</match> to register a keyword with Chrome's address bar."
},
{
content: 'permissions',
description:
'Use the <match>Permissions API</match> to request optional permissions at run time.'
},
{
content: 'runtime',
description:
'Use the <match>Runtime API</match> pass messages, manage extension lifecycle, and access other helper utils.'
},
{
content: 'scripting',
description:
'Use the <match>Scripting API</match> to execute scripts in different contexts.'
},
{
content: 'storage',
description:
'Use the <match>Storage API</match> to store, retrieve, and track changes to user data.'
},
{
content: 'tabs',
description:
'Use the <match>Tabs API</match> to create, update and manipulate tabs.'
},
{
content: 'topSites',
description:
'Use the <match>TopSites API</match> to access the most visited sites that are displayed on the new tab page.'
},
{
content: 'webNavigation',
description:
'Use the <match>WebNavigation API</match> to receive notifications about the status of navigation requests in-flight.'
}
];

View File

@@ -0,0 +1,30 @@
// Popover API https://chromestatus.com/feature/5463833265045504
(async () => {
const nav = document.querySelector('.navigation-rail__links');
const { tip } = await chrome.runtime.sendMessage({ greeting: 'tip' });
const tipWidget = createDomElement(`
<button class="navigation-rail__link" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0; border: none; background: none;>
<div class="navigation-rail__icon">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d='M15 16H9M14.5 9C14.5 7.61929 13.3807 6.5 12 6.5M6 9C6 11.2208 7.2066 13.1599 9 14.1973V18.5C9 19.8807 10.1193 21 11.5 21H12.5C13.8807 21 15 19.8807 15 18.5V14.1973C16.7934 13.1599 18 11.2208 18 9C18 5.68629 15.3137 3 12 3C8.68629 3 6 5.68629 6 9Z'"></path>
</svg>
</div>
<span>Tip</span>
</button>
`);
const popover = createDomElement(
`<div id='tip-popover' popover>${tip}</div>`
);
document.body.append(popover);
nav.append(tipWidget);
})();
function createDomElement(html) {
const dom = new DOMParser().parseFromString(html, 'text/html');
return dom.body.firstElementChild;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,25 @@
{
"manifest_version": 3,
"name": "Open extension API reference",
"version": "1.0.0",
"icons": {
"16": "icon-16.png",
"128": "icon-128.png"
},
"background": {
"service_worker": "service-worker.js",
"type": "module"
},
"minimum_chrome_version": "102",
"omnibox": {
"keyword": "api"
},
"permissions": ["alarms", "storage"],
"content_scripts": [
{
"matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
"js": ["content.js"]
}
],
"host_permissions": ["https://extension-tips.glitch.me/*"]
}

View File

@@ -0,0 +1,2 @@
import './sw-omnibox.js';
import './sw-tips.js';

View File

@@ -0,0 +1,37 @@
import { getApiSuggestions } from './sw-suggestions.js';
console.log('sw-omnibox.js');
// Initialize default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
if (reason === 'install') {
chrome.storage.local.set({
apiSuggestions: ['tabs', 'storage', 'scripting']
});
}
});
const URL_CHROME_EXTENSIONS_DOC =
'https://developer.chrome.com/docs/extensions/reference/';
const NUMBER_OF_PREVIOUS_SEARCHES = 4;
// Displays the suggestions after user starts typing
chrome.omnibox.onInputChanged.addListener(async (input, suggest) => {
const { description, suggestions } = await getApiSuggestions(input);
await chrome.omnibox.setDefaultSuggestion({ description });
suggest(suggestions);
});
// Opens the reference page of the chosen API
chrome.omnibox.onInputEntered.addListener((input) => {
chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
// Saves the latest keyword
updateHistory(input);
});
async function updateHistory(input) {
const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
apiSuggestions.unshift(input);
apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
await chrome.storage.local.set({ apiSuggestions });
}

View File

@@ -0,0 +1,23 @@
import apiList from './api-list.js';
/**
* Returns a list of suggestions and a description for the default suggestion
*/
export async function getApiSuggestions(input) {
const suggestions = apiList.filter((api) => api.content.startsWith(input));
// return suggestions if any exist
if (suggestions.length) {
return {
description: 'Matching Chrome APIs',
suggestions: suggestions
};
}
// return past searches if no match was found
const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
return {
description: 'No matches found. Choose from past searches',
suggestions: apiList.filter((item) => apiSuggestions.includes(item.content))
};
}

View File

@@ -0,0 +1,28 @@
console.log('sw-tips.js');
// Fetch tip & save in storage
const updateTip = async () => {
const response = await fetch('https://extension-tips.glitch.me/tips.json');
const tips = await response.json();
const randomIndex = Math.floor(Math.random() * tips.length);
await chrome.storage.local.set({ tip: tips[randomIndex] });
};
// Create a daily alarm and retrieves the first tip when extension is installed.
chrome.runtime.onInstalled.addListener(({ reason }) => {
if (reason === 'install') {
chrome.alarms.create({ delayInMinutes: 1, periodInMinutes: 1440 });
updateTip();
}
});
// Retrieve tip of the day
chrome.alarms.onAlarm.addListener(updateTip);
// Send tip to content script via messaging
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.greeting === 'tip') {
chrome.storage.local.get('tip').then(sendResponse);
return true;
}
});