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
This commit is contained in:
Michael Lip
2026-03-04 17:18:27 +07:00
committed by GitHub
parent e48ebc8c4a
commit e85e798a37
9 changed files with 197 additions and 0 deletions

View File

@@ -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`.

View File

@@ -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}.`
});
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

View File

@@ -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"
}
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="popup.css" />
</head>
<body>
<h1>Registered Commands</h1>
<div id="commands"></div>
<p id="hint">
Customize shortcuts at
<code>chrome://extensions/shortcuts</code>
</p>
<script src="popup.js"></script>
</body>
</html>

View File

@@ -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();