Skip to content

Commit

Permalink
[New sample] Open Chrome API reference page (omnibox, alarms, messagi…
Browse files Browse the repository at this point in the history
…ng 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
  • Loading branch information
AmySteam authored Aug 21, 2024
1 parent b3ff024 commit 90249bc
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 0 deletions.
81 changes: 81 additions & 0 deletions functional-samples/tutorial.open-api-reference/api-list.js
Original file line number Diff line number Diff line change
@@ -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.'
}
];
30 changes: 30 additions & 0 deletions functional-samples/tutorial.open-api-reference/content.js
Original file line number Diff line number Diff line change
@@ -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;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions functional-samples/tutorial.open-api-reference/manifest.json
Original file line number Diff line number Diff line change
@@ -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/*"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import './sw-omnibox.js';
import './sw-tips.js';
37 changes: 37 additions & 0 deletions functional-samples/tutorial.open-api-reference/sw-omnibox.js
Original file line number Diff line number Diff line change
@@ -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 });
}
23 changes: 23 additions & 0 deletions functional-samples/tutorial.open-api-reference/sw-suggestions.js
Original file line number Diff line number Diff line change
@@ -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))
};
}
28 changes: 28 additions & 0 deletions functional-samples/tutorial.open-api-reference/sw-tips.js
Original file line number Diff line number Diff line change
@@ -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;
}
});

0 comments on commit 90249bc

Please sign in to comment.