Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions api-samples/commands/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# chrome.commands

This sample demonstrates the `chrome.commands` API for adding keyboard shortcuts to Chrome extensions.

## Overview

The extension showcases how to:

- **Define keyboard shortcuts** in the manifest
- **Listen for command events** in the background script
- **Display registered commands** to users
- **Trigger actions** via keyboard shortcuts
- **Customize shortcuts** through Chrome's settings

## Features

### Registered Commands

1. **Open Extension Popup** (`Ctrl+Shift+Y` / `Cmd+Shift+Y`)
- Opens the extension popup using the special `_execute_action` command

2. **Toggle Feature** (`Ctrl+Shift+F` / `Cmd+Shift+F`)
- Toggles a feature on/off with persistent state

3. **Take Screenshot** (`Ctrl+Shift+S` / `Cmd+Shift+S`)
- Captures the current tab and saves as PNG

4. **Show Notification** (`Ctrl+Shift+N` / `Cmd+Shift+N`)
- Displays a test notification

5. **Command Without Shortcut**
- Demonstrates a command that can be assigned a shortcut later

### Interactive UI

- View all registered commands with their shortcuts
- Trigger commands via buttons (alternative to keyboard)
- See real-time feature status with visual indicator
- Direct link to Chrome's keyboard shortcuts settings

## API Usage Examples

### Define commands in manifest.json
```json
{
"commands": {
"toggle-feature": {
"suggested_key": {
"default": "Ctrl+Shift+F",
"mac": "Command+Shift+F"
},
"description": "Toggle a feature on/off"
}
}
}
```

### Listen for commands in background script
```javascript
chrome.commands.onCommand.addListener((command) => {
if (command === 'toggle-feature') {
// Handle the command
}
});
```

### Get all registered commands
```javascript
const commands = await chrome.commands.getAll();
commands.forEach(cmd => {
console.log(`${cmd.name}: ${cmd.shortcut || 'Not set'}`);
});
```

## Special Commands

- **`_execute_action`** - Opens the extension's popup (action)
- **`_execute_browser_action`** - Opens browser action (MV2)
- **`_execute_page_action`** - Opens page action (MV2)

## Customizing Shortcuts

Users can customize keyboard shortcuts:

1. Navigate to `chrome://extensions/shortcuts`
2. Find your extension
3. Click the edit icon next to any command
4. Press the desired key combination

## Keyboard Shortcut Guidelines

- **Modifiers required**: All shortcuts must include `Ctrl` or `Command` (on Mac)
- **Additional modifiers**: Can include `Shift` and `Alt`
- **Maximum shortcuts**: Up to 4 suggested shortcuts per command
- **Platform-specific**: Different shortcuts for Windows/Linux/Mac/ChromeOS

## Permissions

This extension uses:

- `activeTab` - For taking screenshots of the current tab
- `notifications` - For showing notifications
- `storage` - For persisting feature state

## 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. Try the keyboard shortcuts or click the extension icon
4. Customize shortcuts at `chrome://extensions/shortcuts`

## Implementation Notes

- Commands are registered in `manifest.json`
- The background service worker listens for `chrome.commands.onCommand` events
- State is persisted using `chrome.storage.local`
- The popup displays all commands using `chrome.commands.getAll()`
- Screenshots use `chrome.tabs.captureVisibleTab()`

## Learn More

- [chrome.commands API Reference](https://developer.chrome.com/docs/extensions/reference/commands/)
- [Keyboard Shortcuts Guide](https://developer.chrome.com/docs/extensions/mv3/user_interface/#commands)
127 changes: 127 additions & 0 deletions api-samples/commands/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2024 Google LLC
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Track feature state
let featureEnabled = false;

// Listen for command shortcuts
chrome.commands.onCommand.addListener(async (command) => {
console.log(`Command triggered: ${command}`);

switch (command) {
case 'toggle-feature':
await handleToggleFeature();
break;

case 'take-screenshot':
await handleTakeScreenshot();
break;

case 'show-notification':
await handleShowNotification();
break;

case 'command-without-shortcut':
console.log('Command without shortcut was triggered');
showNotification('Command Triggered', 'This command has no default shortcut');
break;

default:
console.log('Unknown command:', command);
}
});

// Handle toggle feature command
async function handleToggleFeature() {
featureEnabled = !featureEnabled;

// Save state to storage
await chrome.storage.local.set({ featureEnabled });

const status = featureEnabled ? 'enabled' : 'disabled';
console.log(`Feature ${status}`);

showNotification(
'Feature Toggled',
`Feature is now ${status}`,
featureEnabled ? 'success' : 'info'
);
}

// Handle take screenshot command
async function handleTakeScreenshot() {
try {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

if (!tab) {
showNotification('Screenshot Failed', 'No active tab found', 'error');
return;
}

const dataUrl = await chrome.tabs.captureVisibleTab(null, { format: 'png' });

// Create a download for the screenshot
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
await chrome.downloads.download({
url: dataUrl,
filename: `screenshot-${timestamp}.png`,
saveAs: false
});

showNotification(
'Screenshot Captured',
`Screenshot of "${tab.title}" saved`,
'success'
);
} catch (error) {
console.error('Screenshot error:', error);
showNotification('Screenshot Failed', error.message, 'error');
}
}

// Handle show notification command
async function handleShowNotification() {
const timestamp = new Date().toLocaleTimeString();
showNotification(
'Keyboard Shortcut Triggered',
`This notification was triggered at ${timestamp}`,
'info'
);
}

// Show a notification
function showNotification(title, message, type = 'info') {
const iconMap = {
success: 'icon48.png',
error: 'icon48.png',
info: 'icon48.png'
};

chrome.notifications.create({
type: 'basic',
iconUrl: iconMap[type] || 'icon48.png',
title: title,
message: message,
priority: 2
});
}

// Load saved state on startup
chrome.runtime.onStartup.addListener(async () => {
const data = await chrome.storage.local.get('featureEnabled');
featureEnabled = data.featureEnabled || false;
console.log('Extension started. Feature enabled:', featureEnabled);
});

// Initialize on install
chrome.runtime.onInstalled.addListener(async () => {
await chrome.storage.local.set({ featureEnabled: false });
console.log('Extension installed. Commands registered.');

// Log all registered commands
const commands = await chrome.commands.getAll();
console.log('Registered commands:', commands);
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added api-samples/commands/icon16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added api-samples/commands/icon48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions api-samples/commands/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "Commands API Demo",
"version": "1.0",
"description": "Demonstrates the chrome.commands API for keyboard shortcuts in extensions.",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
},
"commands": {
"_execute_action": {
"suggested_key": {
"default": "Ctrl+Shift+Y",
"mac": "Command+Shift+Y"
},
"description": "Open the extension popup"
},
"toggle-feature": {
"suggested_key": {
"default": "Ctrl+Shift+F",
"mac": "Command+Shift+F"
},
"description": "Toggle a feature on/off"
},
"take-screenshot": {
"suggested_key": {
"default": "Ctrl+Shift+S",
"mac": "Command+Shift+S"
},
"description": "Take a screenshot of current tab"
},
"show-notification": {
"suggested_key": {
"default": "Ctrl+Shift+N",
"mac": "Command+Shift+N"
},
"description": "Show a notification"
},
"command-without-shortcut": {
"description": "A command without a default keyboard shortcut"
}
},
"permissions": [
"activeTab",
"notifications",
"storage"
],
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
}
Loading