Keyboard Shortcuts and the Omnibox — Power-User Triggers
~12 min · commands, omnibox, keyboard, power-user
Level 0Extension Curious
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Track 3 wired one keyboard shortcut. Lesson 4 broadens the trigger set — multiple commands, the chrome://extensions/shortcuts re-binding page, and the omnibox keyword that turns Chrome's address bar into a ClipDeck search input. Power-user moves that take five lines of manifest each."
chrome.commands Recap
Track 3 Lesson 6 introduced commands.save-clip. The same mechanism scales to up to four user-bindable shortcuts per extension in the stable channel:
Declare each command in manifest.commands with a unique key, a suggested_key, and a description.
Listen with chrome.commands.onCommand.addListener((name) => ...) in the SW.
Users can re-bind any of them via chrome://extensions/shortcuts — Chrome surfaces every extension's commands there.
The special _execute_action command opens the popup; _execute_side_panel opens the panel without an SW listener.
Diagnosing What's Bound
chrome.commands.getAll() returns the current set with the active key for each. Useful in an onInstalled handler — log the commands so you know whether Chrome silently dropped your suggested key (it happens when another extension or Chrome itself already owns the combo):
const cmds = await chrome.commands.getAll();
for (const c of cmds) console.log(c.name, '→', c.shortcut || '(unbound)');
If shortcut is empty, the user (or the install conflict resolution) didn't bind anything. Surface a one-time hint in the onboarding popup so users can find the re-bind page.
The Omnibox API
chrome.omnibox turns Chrome's address bar into your extension's input. The user types a keyword (declared in the manifest), a space, then their query — your SW receives every keystroke and returns suggestions.
Three fields and three events:
Manifest: "omnibox": { "keyword": "clip" }.
chrome.omnibox.onInputStarted — user just typed the keyword + space; fire one-time setup.
chrome.omnibox.onInputChanged(text, suggest) — fired on every keystroke after the keyword. Call suggest([{ content, description }]) with up to ~5 entries.
chrome.omnibox.onInputEntered(text, disposition) — user picked an entry or hit Enter. disposition tells you whether they want a new tab, the current tab, etc.
For ClipDeck, the obvious play: type clip space in the address bar, then a search term; suggestions list matching clips by source title; selecting one opens the source URL in a new tab.
Discoverability of Omnibox
Omnibox is genuinely a power-user feature — most users will not discover it on their own. Surface it in:
The popup help link ("Type clip in the address bar to search saved clips").
The options page (Track 6).
A first-install onboarding tab if you ship one.
Don't over-engineer the omnibox UI — five suggestions, short titles, one detail line. The user picked their address bar for speed; preserve it.
The Trigger Ladder
ClipDeck now has the full ladder:
Floating button on every page (Track 3 Lesson 4) — most discoverable, most intrusive.
Omnibox clip ... for search (this lesson) — power-user retrieval.
Each costs almost nothing to maintain. Together they cover every type of user — the power user, the kbd-shy clicker, the right-click person, the omnibox enthusiast. You ship them all because they each tax you a few lines of code and reach a different audience.
Multiple low-cost triggers reach more users than one perfect trigger. Keyboard, popup, context menu, omnibox — they all cost a few lines and each catches a different habit. Build them all; let the user pick.
The four-shortcut cap. Stable Chrome enforces a four-user-bindable-command limit per extension. If you need more, document the rest as suggestions the user can wire to OS-level shortcuts via Karabiner, AutoHotkey, or similar. Don't try to register five — Chrome will silently drop the fifth at install.
Code
manifest.json — three custom commands + special _execute_action + omnibox keyword·json
{
"commands": {
"save-clip": {
"suggested_key": { "default": "Ctrl+Shift+K", "mac": "Command+Shift+K" },
"description": "Save the current text selection to ClipDeck"
},
"open-panel": {
"suggested_key": { "default": "Ctrl+Shift+P", "mac": "Command+Shift+P" },
"description": "Open the ClipDeck side panel"
},
"toggle-pause": {
"suggested_key": { "default": "Ctrl+Shift+M", "mac": "Command+Shift+M" },
"description": "Pause or resume ClipDeck capture on this tab"
},
"_execute_action": {
"suggested_key": { "default": "Ctrl+Shift+L", "mac": "Command+Shift+L" },
"description": "Open the ClipDeck popup"
}
},
"omnibox": {
"keyword": "clip"
}
}
Update clipdeck/manifest.json's commands and add the omnibox block from the first code block. Replace clipdeck/background.js's command listener with the second code block. Add the third code block (omnibox handlers) to background.js. Reload. Open chrome://extensions/shortcuts and confirm all four ClipDeck commands appear with their suggested keys — rebind one if a conflict shows. Test each: Ctrl+Shift+K saves a clip; Ctrl+Shift+P opens the panel; Ctrl+Shift+M logs a toggle-pause request to the SW console; Ctrl+Shift+L opens the popup. Then click into the address bar, type clip space, then a search term that matches a clip you've saved — suggestions should appear; press Enter on one to open the source URL.
Hint
If a shortcut shows as unbound on the chrome://extensions/shortcuts page, your suggested combo conflicts with another extension or with Chrome's own bindings — pick a different one. The omnibox keyword is matched on the user's exact typed string; if clip does not trigger suggestions, confirm the manifest entry is "omnibox": { "keyword": "clip" } and reload the extension. If suggestions appear but the <match> tags render literally instead of bolding, that's normal in some Chrome versions — the description supports a small subset of XML formatting, but rendering varies. The interaction itself works regardless. Pressing Enter on raw query text is not guaranteed to supply a suggestion URL, so the handler resolves a matching clip and validates http(s). Test current, foreground, and background dispositions.
Progress
Progress is local-only — sign in to sync across devices.