From e85e798a37cd5d985ea8a95afe4e5a3ac6d6eaf6 Mon Sep 17 00:00:00 2001 From: Michael Lip <51033404+theluckystrike@users.noreply.github.com> Date: Wed, 4 Mar 2026 17:18:27 +0700 Subject: [PATCH] feat: add chrome.commands API sample (#1629) Add a new MV3 sample demonstrating the chrome.commands API. The sample registers custom keyboard shortcuts in the manifest, handles command events in the service worker with notifications and badge text feedback, and uses chrome.commands.getAll() in the popup to display all registered shortcuts. Closes #1126 --- api-samples/commands/README.md | 13 ++++++ api-samples/commands/background.js | 41 ++++++++++++++++++ api-samples/commands/images/icon-128.png | Bin 0 -> 727 bytes api-samples/commands/images/icon-16.png | Bin 0 -> 101 bytes api-samples/commands/images/icon-48.png | Bin 0 -> 142 bytes api-samples/commands/manifest.json | 39 +++++++++++++++++ api-samples/commands/popup.css | 52 +++++++++++++++++++++++ api-samples/commands/popup.html | 16 +++++++ api-samples/commands/popup.js | 36 ++++++++++++++++ 9 files changed, 197 insertions(+) create mode 100644 api-samples/commands/README.md create mode 100644 api-samples/commands/background.js create mode 100644 api-samples/commands/images/icon-128.png create mode 100644 api-samples/commands/images/icon-16.png create mode 100644 api-samples/commands/images/icon-48.png create mode 100644 api-samples/commands/manifest.json create mode 100644 api-samples/commands/popup.css create mode 100644 api-samples/commands/popup.html create mode 100644 api-samples/commands/popup.js diff --git a/api-samples/commands/README.md b/api-samples/commands/README.md new file mode 100644 index 00000000..8e186a33 --- /dev/null +++ b/api-samples/commands/README.md @@ -0,0 +1,13 @@ +# chrome.commands + +This sample demonstrates the [`chrome.commands`](https://developer.chrome.com/docs/extensions/reference/api/commands) API by defining custom keyboard shortcuts and responding to command events. + +## Overview + +The extension registers two custom keyboard shortcuts in the manifest and listens for them using `chrome.commands.onCommand`. One command shows a notification, the other toggles a feature on and off with badge text feedback. The popup uses `chrome.commands.getAll()` to display all registered shortcuts and their current key bindings. + +## Running this extension + +1. Clone this repository. +2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked). +3. Use the keyboard shortcuts shown in the popup or customize them at `chrome://extensions/shortcuts`. diff --git a/api-samples/commands/background.js b/api-samples/commands/background.js new file mode 100644 index 00000000..a919c1a3 --- /dev/null +++ b/api-samples/commands/background.js @@ -0,0 +1,41 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +let featureEnabled = false; + +chrome.commands.onCommand.addListener(async (command) => { + if (command === 'run-action') { + chrome.notifications.create({ + type: 'basic', + iconUrl: 'images/icon-128.png', + title: 'Commands API Demo', + message: 'The "run-action" command was triggered.' + }); + } + + if (command === 'toggle-feature') { + featureEnabled = !featureEnabled; + const state = featureEnabled ? 'ON' : 'OFF'; + + chrome.action.setBadgeText({ text: featureEnabled ? 'ON' : '' }); + chrome.action.setBadgeBackgroundColor({ color: '#4688F1' }); + + chrome.notifications.create({ + type: 'basic', + iconUrl: 'images/icon-128.png', + title: 'Feature Toggled', + message: `The feature is now ${state}.` + }); + } +}); diff --git a/api-samples/commands/images/icon-128.png b/api-samples/commands/images/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..58cd488f76b291974d42b3eacaf9bcc3bd68183c GIT binary patch literal 727 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV4COY;uumf=j~-fKbAlN=8Jo! z+8-Z#qEN)No<(cb_IX!!9-sHWLdA0u3bL=P{{Q1m-TxP{=_P^T{r5`U#7n=D$*KSN z@%{_O7i+$(GY($4i~?$N`su1v{r}7t)}NX(^d++EAikk#;_2B;Yd7U(Hh$un5+ i3fM9*++^^)bk%o(V#KcM+X??b3Orr?T-G@yGywpA4lJzz literal 0 HcmV?d00001 diff --git a/api-samples/commands/manifest.json b/api-samples/commands/manifest.json new file mode 100644 index 00000000..c9e22af3 --- /dev/null +++ b/api-samples/commands/manifest.json @@ -0,0 +1,39 @@ +{ + "name": "Commands API Demo", + "version": "1.0", + "description": "Uses the chrome.commands API to define keyboard shortcuts and handle command events.", + "manifest_version": 3, + "background": { + "service_worker": "background.js" + }, + "permissions": ["notifications"], + "icons": { + "16": "images/icon-16.png", + "48": "images/icon-48.png", + "128": "images/icon-128.png" + }, + "action": { + "default_popup": "popup.html", + "default_icon": { + "16": "images/icon-16.png", + "48": "images/icon-48.png", + "128": "images/icon-128.png" + } + }, + "commands": { + "run-action": { + "suggested_key": { + "default": "Ctrl+Shift+Y", + "mac": "Command+Shift+Y" + }, + "description": "Run the sample action" + }, + "toggle-feature": { + "suggested_key": { + "default": "Ctrl+Shift+U", + "mac": "Command+Shift+U" + }, + "description": "Toggle a feature on or off" + } + } +} diff --git a/api-samples/commands/popup.css b/api-samples/commands/popup.css new file mode 100644 index 00000000..ba8ccd5d --- /dev/null +++ b/api-samples/commands/popup.css @@ -0,0 +1,52 @@ +body { + width: 300px; + padding: 12px 16px; + font-family: system-ui, sans-serif; + font-size: 14px; + color: #202124; +} + +h1 { + font-size: 16px; + font-weight: 600; + margin: 0 0 12px; +} + +.command-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 0; + border-bottom: 1px solid #e8eaed; +} + +.command-row:last-child { + border-bottom: none; +} + +.command-name { + color: #3c4043; +} + +kbd { + background: #f1f3f4; + border: 1px solid #dadce0; + border-radius: 4px; + padding: 2px 8px; + font-family: monospace; + font-size: 12px; + color: #5f6368; +} + +#hint { + margin: 12px 0 0; + font-size: 12px; + color: #80868b; +} + +#hint code { + background: #f1f3f4; + padding: 1px 4px; + border-radius: 3px; + font-size: 11px; +} diff --git a/api-samples/commands/popup.html b/api-samples/commands/popup.html new file mode 100644 index 00000000..2c140820 --- /dev/null +++ b/api-samples/commands/popup.html @@ -0,0 +1,16 @@ + + + + + + + +

Registered Commands

+
+

+ Customize shortcuts at + chrome://extensions/shortcuts +

+ + + diff --git a/api-samples/commands/popup.js b/api-samples/commands/popup.js new file mode 100644 index 00000000..8b8b407d --- /dev/null +++ b/api-samples/commands/popup.js @@ -0,0 +1,36 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +async function listCommands() { + const commands = await chrome.commands.getAll(); + const container = document.getElementById('commands'); + + for (const command of commands) { + const row = document.createElement('div'); + row.className = 'command-row'; + + const name = document.createElement('span'); + name.className = 'command-name'; + name.textContent = command.description || command.name; + + const shortcut = document.createElement('kbd'); + shortcut.textContent = command.shortcut || 'Not set'; + + row.appendChild(name); + row.appendChild(shortcut); + container.appendChild(row); + } +} + +listCommands();