Add mole game tutorial (#1433)

LGTN
This commit is contained in:
Oliver Dunk
2025-04-02 18:10:45 +01:00
committed by GitHub
parent 8cf0e9a827
commit 6116518873
8 changed files with 105 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
# Mole Game
This mole game is based on a YouTube tutorial in our Chrome Extensions series. Watch it [here](https://goo.gle/Chrome-Ext).
<img src="mole-game.png" width=500>
## Overview
In the sample, moles periodically appear from pipes in the browser toolbar. You score points by clicking the icon before the mole disappears.
If enabled, a browser tab is closed if you miss one of the moles.
## Implementation Notes
Each icon in the browser toolbar is a seperate extension. The extensions communicate using the `chrome.runtime.sendMessage` API and the `chrome.runtime.onMessageExternal` event.
To discover mole extensions, the controller extension uses the `chrome.management` API.
By default, the tab closing behavior is disabled. You can enable this by commenting out the line in `mole/service-worker.js`.
## Running this extension
1. Clone this repository.
1. Make several copies of the `mole` directory.
1. Load the `controller` directory and all mole directories in Chrome as [unpacked extensions](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
1. Wait for a mole to appear!

View File

@@ -0,0 +1,10 @@
{
"name": "mole controller",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "service-worker.js"
},
"permissions": ["management", "alarms"],
"action": {}
}

View File

@@ -0,0 +1,19 @@
chrome.runtime.onInstalled.addListener(() => {
chrome.alarms.create({ periodInMinutes: (1 / 60) * 3 });
});
chrome.alarms.onAlarm.addListener(async () => {
const extensions = await chrome.management.getAll();
const moles = extensions.filter((e) => e.name === 'mole');
const randomIndex = Math.floor(Math.random() * moles.length);
chrome.runtime.sendMessage(moles[randomIndex].id, { id: chrome.runtime.id });
});
let counter = 0;
chrome.runtime.onMessageExternal.addListener(() => {
counter++;
chrome.action.setBadgeText({ text: `${counter}` });
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,12 @@
{
"name": "mole",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "service-worker.js"
},
"action": {
"default_icon": "icon-empty.png"
},
"permissions": ["management", "tabs"]
}

View File

@@ -0,0 +1,38 @@
let failTimeout;
let moleShowing = false;
let controllerId;
chrome.action.onClicked.addListener(() => {
if (!moleShowing) return;
chrome.runtime.sendMessage(controllerId, 'success');
hideMole();
failTimeout && clearTimeout(failTimeout);
failTimeout = undefined;
});
function showMole() {
chrome.action.setIcon({ path: 'icon-mole.png' });
moleShowing = true;
}
function hideMole() {
chrome.action.setIcon({ path: 'icon-empty.png' });
moleShowing = false;
}
chrome.runtime.onMessageExternal.addListener((msg) => {
controllerId = msg.id;
showMole();
failTimeout = setTimeout(async () => {
hideMole();
const tabs = await chrome.tabs.query({});
const eligibleTabs = tabs.filter((t) => t.title.includes('Example'));
if (eligibleTabs.length > 0) {
// const tabToClose = Math.floor(Math.random() * eligibleTabs.length);
// chrome.tabs.remove(eligibleTabs[tabToClose].id);
}
}, 2000);
});